Partial implementation of transfer
This commit is contained in:
parent
316604d3d5
commit
1c13945b8a
|
@ -19,57 +19,57 @@ import org.slf4j.event.Level;
|
||||||
*/
|
*/
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
|
|
||||||
public static final String VERSION = "1.0.0";
|
public static final String VERSION = "1.0.0";
|
||||||
private static TdClusterManager clusterManager;
|
private static TdClusterManager clusterManager;
|
||||||
|
|
||||||
private static TransferService transferService;
|
private static TransferService transferService;
|
||||||
private static LogService logService;
|
private static LogService logService;
|
||||||
|
|
||||||
private static Scene scene;
|
private static Scene scene;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException, CantLoadLibrary {
|
public void start(Stage stage) throws IOException, CantLoadLibrary {
|
||||||
Init.start();
|
Init.start();
|
||||||
clusterManager = new TdClusterManager(null, null, Vertx.vertx(), null);
|
clusterManager = new TdClusterManager(null, null, Vertx.vertx(), null);
|
||||||
|
|
||||||
logService = new LogServiceImpl();
|
logService = new LogServiceImpl();
|
||||||
logService.append(Level.INFO, "Initializing");
|
logService.append(Level.INFO, "Initializing");
|
||||||
|
|
||||||
transferService = new TransferServiceImpl(clusterManager);
|
transferService = new TransferServiceImpl(clusterManager);
|
||||||
|
|
||||||
transferService.setApiId(94575);
|
transferService.setApiId(94575);
|
||||||
transferService.setApiHash("a3406de8d171bb422bb6ddf3bbd800e2");
|
transferService.setApiHash("a3406de8d171bb422bb6ddf3bbd800e2");
|
||||||
|
|
||||||
scene = new Scene(loadFXML("primary"), 800, 600);
|
scene = new Scene(loadFXML("primary"), 800, 600);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.show();
|
stage.show();
|
||||||
logService.append(Level.INFO, "Initialized");
|
logService.append(Level.INFO, "Initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws Exception {
|
public void stop() throws Exception {
|
||||||
App.getTransferService().quit().block(Duration.ofSeconds(15));
|
App.getTransferService().quit().block(Duration.ofSeconds(15));
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setRoot(String fxml) throws IOException {
|
static void setRoot(String fxml) throws IOException {
|
||||||
scene.setRoot(loadFXML(fxml));
|
scene.setRoot(loadFXML(fxml));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Parent loadFXML(String fxml) throws IOException {
|
private static Parent loadFXML(String fxml) throws IOException {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
|
||||||
return fxmlLoader.load();
|
return fxmlLoader.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
launch();
|
launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TransferService getTransferService() {
|
public static TransferService getTransferService() {
|
||||||
return transferService;
|
return transferService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogService getLogService() {
|
public static LogService getLogService() {
|
||||||
return logService;
|
return logService;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,12 @@
|
||||||
package it.cavallium;
|
package it.cavallium;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi;
|
||||||
|
import it.tdlight.tdlibsession.td.TdResult;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.Dialog;
|
import javafx.scene.control.Dialog;
|
||||||
|
import org.slf4j.event.Level;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
public class MonoFxUtils {
|
public class MonoFxUtils {
|
||||||
|
@ -46,4 +49,12 @@ public class MonoFxUtils {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T extends TdApi.Object> Mono<T> orElseLogSkipError(TdResult<T> optional) {
|
||||||
|
if (optional.failed()) {
|
||||||
|
App.getLogService().append(Level.ERROR, "Received TDLib error: " + optional.cause());
|
||||||
|
return Mono.empty();
|
||||||
|
}
|
||||||
|
return Mono.just(optional.result());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,10 @@ import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.ChoiceBox;
|
import javafx.scene.control.ChoiceBox;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.Dialog;
|
import javafx.scene.control.Dialog;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.control.ProgressBar;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.control.TextInputDialog;
|
import javafx.scene.control.TextInputDialog;
|
||||||
import javafx.scene.input.InputMethodEvent;
|
import javafx.scene.input.InputMethodEvent;
|
||||||
|
@ -33,312 +36,341 @@ import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
public class PrimaryController {
|
public class PrimaryController {
|
||||||
|
|
||||||
@FXML private Pane main;
|
public Label statusTxt;
|
||||||
@FXML private TextField phoneNumber;
|
public ProgressBar statusBar;
|
||||||
@FXML private ListView<Text> userbotsList;
|
public Label statusPercentage;
|
||||||
@FXML private ChoiceBox<BaseChatInfo> sourceGroupCombo;
|
public TableView<UserStatus> statusTable;
|
||||||
@FXML private ChoiceBox<BaseChatInfo> destGroupCombo;
|
@FXML private Pane main;
|
||||||
@FXML private ListView<LogEntry> log;
|
@FXML private TextField phoneNumber;
|
||||||
|
@FXML private ListView<Text> userbotsList;
|
||||||
|
@FXML private ChoiceBox<BaseChatInfo> sourceGroupCombo;
|
||||||
|
@FXML private ChoiceBox<BaseChatInfo> destGroupCombo;
|
||||||
|
@FXML private ListView<LogEntry> log;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
for (PhoneNumber number : App.getTransferService().getPhoneNumbers()) {
|
for (PhoneNumber number : App.getTransferService().getPhoneNumbers()) {
|
||||||
userbotsList
|
userbotsList
|
||||||
.getItems()
|
.getItems()
|
||||||
.add(0,
|
.add(0,
|
||||||
new Text(getUserbotPrettyPrintPhoneNumber(number))
|
new Text(getUserbotPrettyPrintPhoneNumber(number))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var srcItems = sourceGroupCombo.getItems();
|
var srcItems = sourceGroupCombo.getItems();
|
||||||
srcItems.clear();
|
srcItems.clear();
|
||||||
for (BaseChatInfo originSupergroups : App.getTransferService().getAdminSupergroups(true, false)) {
|
for (BaseChatInfo originSupergroups : App.getTransferService().getAdminSupergroups(true, false)) {
|
||||||
srcItems.add(originSupergroups);
|
srcItems.add(originSupergroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
var destItems = destGroupCombo.getItems();
|
var destItems = destGroupCombo.getItems();
|
||||||
destItems.clear();
|
destItems.clear();
|
||||||
for (BaseChatInfo destSupergroups : App.getTransferService().getAdminSupergroups(false, true)) {
|
for (BaseChatInfo destSupergroups : App.getTransferService().getAdminSupergroups(false, true)) {
|
||||||
destItems.add(destSupergroups);
|
destItems.add(destSupergroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
App
|
App
|
||||||
.getTransferService()
|
.getTransferService()
|
||||||
.subscribeAdminSupergroups()
|
.subscribeAdminSupergroups()
|
||||||
.subscribeOn(Schedulers.single())
|
.subscribeOn(Schedulers.single())
|
||||||
.subscribe((supergroupInfo) -> {
|
.subscribe((supergroupInfo) -> {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
var srcItems2 = sourceGroupCombo.getItems();
|
var srcItems2 = sourceGroupCombo.getItems();
|
||||||
var destItems2 = destGroupCombo.getItems();
|
var destItems2 = destGroupCombo.getItems();
|
||||||
if (userbotsList.getItems().isEmpty()) {
|
if (userbotsList.getItems().isEmpty()) {
|
||||||
srcItems2.clear();
|
srcItems2.clear();
|
||||||
destItems2.clear();
|
destItems2.clear();
|
||||||
} else {
|
} else {
|
||||||
boolean added = false;
|
boolean added = false;
|
||||||
if (supergroupInfo.getItem().canRestrictMembers()) {
|
if (supergroupInfo.getItem().canRestrictMembers()) {
|
||||||
if (!srcItems2.contains(supergroupInfo.getItem().getBaseChatInfo())) {
|
if (!srcItems2.contains(supergroupInfo.getItem().getBaseChatInfo())) {
|
||||||
added |= srcItems2.add(supergroupInfo.getItem().getBaseChatInfo());
|
added |= srcItems2.add(supergroupInfo.getItem().getBaseChatInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (supergroupInfo.getItem().canInviteUsers()) {
|
if (supergroupInfo.getItem().canInviteUsers()) {
|
||||||
if (!destItems2.contains(supergroupInfo.getItem().getBaseChatInfo())) {
|
if (!destItems2.contains(supergroupInfo.getItem().getBaseChatInfo())) {
|
||||||
added |= destItems2.add(supergroupInfo.getItem().getBaseChatInfo());
|
added |= destItems2.add(supergroupInfo.getItem().getBaseChatInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (added) {
|
if (added) {
|
||||||
App.getLogService().append(Level.INFO, "Found group \"" + supergroupInfo.getItem().getBaseChatInfo().getTitle() + "\"");
|
App.getLogService().append(Level.INFO, "Found group \"" + supergroupInfo.getItem().getBaseChatInfo().getTitle() + "\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
App.getLogService().listenUpdates().subscribeOn(Schedulers.boundedElastic()).flatMap(logString -> MonoFxUtils.runLater(() -> {
|
App.getLogService().listenUpdates().subscribeOn(Schedulers.boundedElastic()).flatMap(logString -> MonoFxUtils.runLater(() -> {
|
||||||
if (log.getItems().size() >= App.getLogService().getMaxSize()) {
|
if (log.getItems().size() >= App.getLogService().getMaxSize()) {
|
||||||
log.getItems().remove(0);
|
log.getItems().remove(0);
|
||||||
}
|
}
|
||||||
log.getItems().add(logString);
|
log.getItems().add(logString);
|
||||||
log.scrollTo(logString);
|
log.scrollTo(logString);
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
})).subscribe();
|
})).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disableClicks() {
|
private void disableClicks() {
|
||||||
main.setDisable(true);
|
main.setDisable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableClicks() {
|
private void enableClicks() {
|
||||||
main.setDisable(false);
|
main.setDisable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void openSettings(ActionEvent actionEvent) throws IOException {
|
public void openSettings(ActionEvent actionEvent) throws IOException {
|
||||||
App.setRoot("settings");
|
App.setRoot("settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void quit(ActionEvent actionEvent) {
|
public void quit(ActionEvent actionEvent) {
|
||||||
Platform.exit();
|
Platform.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void addUserbot(ActionEvent actionEvent) {
|
public void addUserbot(ActionEvent actionEvent) {
|
||||||
var eventSource = (Node) actionEvent.getSource();
|
var eventSource = (Node) actionEvent.getSource();
|
||||||
var phoneNumberText = phoneNumber.getText();
|
var phoneNumberText = phoneNumber.getText();
|
||||||
try {
|
try {
|
||||||
var phoneNumber = getUserbotPhoneNumber(phoneNumberText);
|
var phoneNumber = getUserbotPhoneNumber(phoneNumberText);
|
||||||
var phoneNumberPrettyText = getUserbotPrettyPrintPhoneNumber(phoneNumber);
|
var phoneNumberPrettyText = getUserbotPrettyPrintPhoneNumber(phoneNumber);
|
||||||
|
|
||||||
disableClicks();
|
disableClicks();
|
||||||
App.getTransferService().addUserbot(phoneNumber, (client) -> MonoFxUtils.runLater(() -> {
|
App.getTransferService().addUserbot(phoneNumber, (client) -> MonoFxUtils.runLater(() -> {
|
||||||
var authCodeAlert = new TextInputDialog();
|
var authCodeAlert = new TextInputDialog();
|
||||||
authCodeAlert.setHeaderText("Insert authentication code that you received");
|
authCodeAlert.setHeaderText("Insert authentication code that you received");
|
||||||
return PrimaryController.<Integer>loopAlert(authCodeAlert, (authCodeText) -> {
|
return PrimaryController.<Integer>loopAlert(authCodeAlert, (authCodeText) -> {
|
||||||
if (authCodeText.matches("^[0-9]{3,8}$")) {
|
if (authCodeText.matches("^[0-9]{3,8}$")) {
|
||||||
try {
|
try {
|
||||||
int authCode = Integer.parseUnsignedInt(authCodeText);
|
int authCode = Integer.parseUnsignedInt(authCodeText);
|
||||||
return Mono.just(authCode);
|
return Mono.just(authCode);
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
var alert = new Alert(AlertType.ERROR, "Can't parse authentication code number", ButtonType.CLOSE);
|
var alert = new Alert(AlertType.ERROR, "Can't parse authentication code number", ButtonType.CLOSE);
|
||||||
return MonoFxUtils.showAndWait(alert).then(Mono.empty());
|
return MonoFxUtils.showAndWait(alert).then(Mono.empty());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var alert = new Alert(AlertType.ERROR, "Wrong authentication code number format", ButtonType.CLOSE);
|
var alert = new Alert(AlertType.ERROR, "Wrong authentication code number format", ButtonType.CLOSE);
|
||||||
return MonoFxUtils.showAndWait(alert).then(Mono.empty());
|
return MonoFxUtils.showAndWait(alert).then(Mono.empty());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}), (client, hint) -> MonoFxUtils.runLater(() -> {
|
}), (client, hint) -> MonoFxUtils.runLater(() -> {
|
||||||
String hintText = "";
|
String hintText = "";
|
||||||
if (hint != null && !hint.isBlank()) {
|
if (hint != null && !hint.isBlank()) {
|
||||||
hintText = " (hint: " + hint + ")";
|
hintText = " (hint: " + hint + ")";
|
||||||
}
|
}
|
||||||
var otpAlert = new TextInputDialog();
|
var otpAlert = new TextInputDialog();
|
||||||
otpAlert.setHeaderText("Insert user password" + hintText + ":");
|
otpAlert.setHeaderText("Insert user password" + hintText + ":");
|
||||||
return loopAlert(otpAlert, (otpText) -> {
|
return loopAlert(otpAlert, (otpText) -> {
|
||||||
if (otpText.length() > 0) {
|
if (otpText.length() > 0) {
|
||||||
return Mono.just(otpText);
|
return Mono.just(otpText);
|
||||||
} else {
|
} else {
|
||||||
var alert = new Alert(AlertType.ERROR, "Wrong user password format", ButtonType.CLOSE);
|
var alert = new Alert(AlertType.ERROR, "Wrong user password format", ButtonType.CLOSE);
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}), () -> {
|
}), () -> {
|
||||||
// closed, remove from list
|
// closed, remove from list
|
||||||
return MonoFxUtils.runLater(() -> {
|
return MonoFxUtils.runLater(() -> {
|
||||||
userbotsList.getItems().removeIf(textBox -> {
|
userbotsList.getItems().removeIf(textBox -> {
|
||||||
try {
|
try {
|
||||||
return getUserbotPhoneNumber(textBox.getText()).equals(phoneNumber);
|
return getUserbotPhoneNumber(textBox.getText()).equals(phoneNumber);
|
||||||
} catch (NumberParseException e) {
|
} catch (NumberParseException e) {
|
||||||
// Can't happen
|
// Can't happen
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (userbotsList.getItems().isEmpty()) {
|
if (userbotsList.getItems().isEmpty()) {
|
||||||
this.sourceGroupCombo.getItems().clear();
|
this.sourceGroupCombo.getItems().clear();
|
||||||
this.destGroupCombo.getItems().clear();
|
this.destGroupCombo.getItems().clear();
|
||||||
}
|
}
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
});
|
});
|
||||||
}).handle((result, sink) -> {
|
}).handle((result, sink) -> {
|
||||||
if (result.failed()) {
|
if (result.failed()) {
|
||||||
sink.error(new Exception(result.getErrorMessage()));
|
sink.error(new Exception(result.getErrorMessage()));
|
||||||
} else {
|
} else {
|
||||||
sink.next(result);
|
sink.next(result);
|
||||||
}
|
}
|
||||||
}).flatMap((_v) -> MonoFxUtils.runLater(() -> {
|
}).flatMap((_v) -> MonoFxUtils.runLater(() -> {
|
||||||
var alert = new Alert(AlertType.INFORMATION,
|
var alert = new Alert(AlertType.INFORMATION,
|
||||||
"Added userbot " + PhoneNumberUtil
|
"Added userbot " + PhoneNumberUtil
|
||||||
.getInstance()
|
.getInstance()
|
||||||
.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL),
|
.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL),
|
||||||
ButtonType.CLOSE
|
ButtonType.CLOSE
|
||||||
);
|
);
|
||||||
return MonoFxUtils.showAndWait(alert);
|
return MonoFxUtils.showAndWait(alert);
|
||||||
})).doOnSuccess((_v) -> {
|
})).doOnSuccess((_v) -> {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
userbotsList
|
userbotsList
|
||||||
.getItems()
|
.getItems()
|
||||||
.add(0,
|
.add(0,
|
||||||
new Text(phoneNumberPrettyText)
|
new Text(phoneNumberPrettyText)
|
||||||
);
|
);
|
||||||
this.phoneNumber.setText("");
|
this.phoneNumber.setText("");
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.doOnTerminate(this::enableClicks)
|
.doOnTerminate(this::enableClicks)
|
||||||
.subscribe(_v -> {}, error -> {
|
.subscribe(_v -> {}, error -> {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
var alert = new Alert(AlertType.ERROR,
|
var alert = new Alert(AlertType.ERROR,
|
||||||
"Error while adding the userbot " + PhoneNumberUtil
|
"Error while adding the userbot " + PhoneNumberUtil
|
||||||
.getInstance()
|
.getInstance()
|
||||||
.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL),
|
.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL),
|
||||||
ButtonType.CLOSE
|
ButtonType.CLOSE
|
||||||
);
|
);
|
||||||
alert.setContentText(error.getLocalizedMessage());
|
alert.setContentText(error.getLocalizedMessage());
|
||||||
MonoFxUtils.showAndWait(alert).subscribe();
|
MonoFxUtils.showAndWait(alert).subscribe();
|
||||||
|
|
||||||
enableClicks();
|
enableClicks();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (NumberFormatException | NumberParseException ex) {
|
} catch (NumberFormatException | NumberParseException ex) {
|
||||||
var alert = new Alert(AlertType.ERROR, "Can't parse phone number format", ButtonType.CLOSE);
|
var alert = new Alert(AlertType.ERROR, "Can't parse phone number format", ButtonType.CLOSE);
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getUserbotPrettyPrintPhoneNumber(PhoneNumber phoneNumber) {
|
private String getUserbotPrettyPrintPhoneNumber(PhoneNumber phoneNumber) {
|
||||||
return PhoneNumberUtil
|
return PhoneNumberUtil
|
||||||
.getInstance()
|
.getInstance()
|
||||||
.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL);
|
.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PhoneNumber getUserbotPhoneNumber(String phoneNumberText) throws NumberParseException {
|
public static PhoneNumber getUserbotPhoneNumber(String phoneNumberText) throws NumberParseException {
|
||||||
return PhoneNumberUtil
|
return PhoneNumberUtil
|
||||||
.getInstance()
|
.getInstance()
|
||||||
.parse(phoneNumberText, PhoneNumberUtil.getCountryMobileToken(1));
|
.parse(phoneNumberText, PhoneNumberUtil.getCountryMobileToken(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> Mono<T> loopAlert(TextInputDialog alert, Function<String, Mono<T>> resultParser) {
|
private static <T> Mono<T> loopAlert(TextInputDialog alert, Function<String, Mono<T>> resultParser) {
|
||||||
return MonoFxUtils.showAndWaitOpt(alert)
|
return MonoFxUtils.showAndWaitOpt(alert)
|
||||||
.flatMap(resultOpt -> {
|
.flatMap(resultOpt -> {
|
||||||
if (resultOpt.isEmpty()) {
|
if (resultOpt.isEmpty()) {
|
||||||
return Mono.just(Optional.<T>empty());
|
return Mono.just(Optional.<T>empty());
|
||||||
} else {
|
} else {
|
||||||
return resultParser.apply(resultOpt.get()).filter(Objects::nonNull).map(Optional::of);
|
return resultParser.apply(resultOpt.get()).filter(Objects::nonNull).map(Optional::of);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.repeatWhen(s -> s.takeWhile(n -> n == 0))
|
.repeatWhen(s -> s.takeWhile(n -> n == 0))
|
||||||
.last(Optional.empty())
|
.last(Optional.empty())
|
||||||
.flatMap(opt -> opt.map(Mono::just).orElseGet(Mono::empty));
|
.flatMap(opt -> opt.map(Mono::just).orElseGet(Mono::empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void removeUserbot(ActionEvent actionEvent) {
|
public void removeUserbot(ActionEvent actionEvent) {
|
||||||
var selectedItem = userbotsList.getSelectionModel().getSelectedItem();
|
var selectedItem = userbotsList.getSelectionModel().getSelectedItem();
|
||||||
if (selectedItem != null) {
|
if (selectedItem != null) {
|
||||||
disableClicks();
|
disableClicks();
|
||||||
try {
|
try {
|
||||||
var phoneNumber = getUserbotPhoneNumber(selectedItem.getText());
|
var phoneNumber = getUserbotPhoneNumber(selectedItem.getText());
|
||||||
|
|
||||||
App.getTransferService()
|
App.getTransferService()
|
||||||
.closeUserbot(phoneNumber)
|
.closeUserbot(phoneNumber)
|
||||||
.subscribeOn(Schedulers.boundedElastic())
|
.subscribeOn(Schedulers.boundedElastic())
|
||||||
.then(MonoFxUtils.runLater(() -> {
|
.then(MonoFxUtils.runLater(() -> {
|
||||||
// closed, remove from list
|
// closed, remove from list
|
||||||
userbotsList.getItems().remove(selectedItem);
|
userbotsList.getItems().remove(selectedItem);
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}))
|
}))
|
||||||
.doOnTerminate(this::enableClicks)
|
.doOnTerminate(this::enableClicks)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
} catch (NumberParseException e) {
|
} catch (NumberParseException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onSourceGroupAction(ActionEvent actionEvent) {
|
public void onSourceGroupAction(ActionEvent actionEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onSourceGroupHiding(Event event) {
|
public void onSourceGroupHiding(Event event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onSourceGroupTextChanged(InputMethodEvent inputMethodEvent) {
|
public void onSourceGroupTextChanged(InputMethodEvent inputMethodEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onSourceGroupShowing(Event event) {
|
public void onSourceGroupShowing(Event event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onDestGroupAction(ActionEvent actionEvent) {
|
public void onDestGroupAction(ActionEvent actionEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onDestGroupHiding(Event event) {
|
public void onDestGroupHiding(Event event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onDestGroupTextChanged(InputMethodEvent inputMethodEvent) {
|
public void onDestGroupTextChanged(InputMethodEvent inputMethodEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onDestGroupShowing(Event event) {
|
public void onDestGroupShowing(Event event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@SuppressWarnings("ConstantConditions")
|
||||||
public void onDoTransfer(ActionEvent actionEvent) {
|
@FXML
|
||||||
if (sourceGroupCombo.getSelectionModel().getSelectedItem() == null || !(sourceGroupCombo.getSelectionModel().getSelectedItem() instanceof BaseChatInfo)) {
|
public void onDoTransfer(ActionEvent actionEvent) {
|
||||||
MonoFxUtils
|
if (sourceGroupCombo.getSelectionModel().getSelectedItem() == null
|
||||||
.showAndWait(new Alert(AlertType.ERROR, "You must select a source group", ButtonType.CLOSE))
|
|| !(sourceGroupCombo.getSelectionModel().getSelectedItem() instanceof BaseChatInfo)) {
|
||||||
.subscribe();
|
MonoFxUtils
|
||||||
return;
|
.showAndWait(new Alert(AlertType.ERROR, "You must select a source group", ButtonType.CLOSE))
|
||||||
}
|
.subscribe();
|
||||||
if (destGroupCombo.getSelectionModel().getSelectedItem() == null || !(destGroupCombo.getSelectionModel().getSelectedItem() instanceof BaseChatInfo)) {
|
return;
|
||||||
MonoFxUtils
|
}
|
||||||
.showAndWait(new Alert(AlertType.ERROR, "You must select a destination group", ButtonType.CLOSE))
|
if (destGroupCombo.getSelectionModel().getSelectedItem() == null
|
||||||
.subscribe();
|
|| !(destGroupCombo.getSelectionModel().getSelectedItem() instanceof BaseChatInfo)) {
|
||||||
return;
|
MonoFxUtils
|
||||||
}
|
.showAndWait(new Alert(AlertType.ERROR, "You must select a destination group", ButtonType.CLOSE))
|
||||||
disableClicks();
|
.subscribe();
|
||||||
App
|
return;
|
||||||
.getTransferService()
|
}
|
||||||
.transferMembers(sourceGroupCombo.getSelectionModel().getSelectedItem(),
|
disableClicks();
|
||||||
destGroupCombo.getSelectionModel().getSelectedItem(),
|
statusTable.getItems().clear();
|
||||||
userStatus -> {
|
App
|
||||||
return Mono.empty();
|
.getTransferService()
|
||||||
}
|
.transferMembers(sourceGroupCombo.getSelectionModel().getSelectedItem(),
|
||||||
)
|
destGroupCombo.getSelectionModel().getSelectedItem(),
|
||||||
.onErrorResume(error -> MonoFxUtils.runLater(() -> {
|
userStatus -> MonoFxUtils.runLater(() -> {
|
||||||
var alert = new Alert(AlertType.ERROR, "Error during transfer", ButtonType.CLOSE);
|
if (statusTable.getItems().contains(userStatus)) {
|
||||||
alert.setContentText(error.getLocalizedMessage());
|
statusTable.getItems().remove(userStatus);
|
||||||
return MonoFxUtils.showAndWait(alert);
|
}
|
||||||
}).then())
|
statusTable.getItems().add(userStatus);
|
||||||
.subscribe(_v -> {}, _v -> {
|
return Mono.empty();
|
||||||
|
}),
|
||||||
|
percentage -> MonoFxUtils.runLater(() -> {
|
||||||
|
statusPercentage.setText(percentage + "%");
|
||||||
|
statusBar.setProgress(((double) percentage / 100d));
|
||||||
|
return Mono.empty();
|
||||||
|
}),
|
||||||
|
phaseDescription -> MonoFxUtils.runLater(() -> {
|
||||||
|
App.getLogService().append(Level.INFO, phaseDescription);
|
||||||
|
statusTxt.setText(phaseDescription);
|
||||||
|
return Mono.empty();
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.onErrorResume(error -> MonoFxUtils.runLater(() -> {
|
||||||
|
var alert = new Alert(AlertType.ERROR, "Error during transfer", ButtonType.CLOSE);
|
||||||
|
alert.setContentText(error.getLocalizedMessage());
|
||||||
|
return MonoFxUtils.showAndWait(alert);
|
||||||
|
}).then())
|
||||||
|
.doOnTerminate(() -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
statusTxt.setText("");
|
||||||
|
statusPercentage.setText("0%");
|
||||||
|
statusBar.setProgress(0d);
|
||||||
|
enableClicks();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.subscribe(_v -> {
|
||||||
|
}, _v -> {
|
||||||
|
|
||||||
}, () -> {
|
}, () -> {});
|
||||||
enableClicks();
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import javafx.fxml.FXML;
|
||||||
|
|
||||||
public class SecondaryController {
|
public class SecondaryController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void switchToPrimary() throws IOException {
|
private void switchToPrimary() throws IOException {
|
||||||
App.setRoot("primary");
|
App.setRoot("primary");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ import it.tdlight.jni.TdApi.AuthorizationState;
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
||||||
import it.tdlight.jni.TdApi.Chat;
|
import it.tdlight.jni.TdApi.Chat;
|
||||||
import it.tdlight.jni.TdApi.ChatMemberStatusAdministrator;
|
import it.tdlight.jni.TdApi.ChatMemberStatusAdministrator;
|
||||||
|
import it.tdlight.jni.TdApi.ChatMemberStatusCreator;
|
||||||
import it.tdlight.jni.TdApi.GetSupergroupFullInfo;
|
import it.tdlight.jni.TdApi.GetSupergroupFullInfo;
|
||||||
import it.tdlight.jni.TdApi.Object;
|
import it.tdlight.jni.TdApi.Object;
|
||||||
import it.tdlight.jni.TdApi.Supergroup;
|
import it.tdlight.jni.TdApi.Supergroup;
|
||||||
|
@ -18,6 +19,7 @@ import it.tdlight.tdlibsession.td.TdResult;
|
||||||
import it.tdlight.tdlibsession.td.easy.AsyncTdEasy;
|
import it.tdlight.tdlibsession.td.easy.AsyncTdEasy;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.StringJoiner;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import reactor.core.publisher.EmitterProcessor;
|
import reactor.core.publisher.EmitterProcessor;
|
||||||
|
@ -28,6 +30,7 @@ import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
public class TransferClient {
|
public class TransferClient {
|
||||||
|
|
||||||
|
private final String alias;
|
||||||
private final AsyncTdEasy client;
|
private final AsyncTdEasy client;
|
||||||
private final ConcurrentHashMap<Integer, Supergroup> supergroupInfos = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Integer, Supergroup> supergroupInfos = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentHashMap<Integer, SupergroupFullInfo> supergroupFullInfos = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Integer, SupergroupFullInfo> supergroupFullInfos = new ConcurrentHashMap<>();
|
||||||
|
@ -35,7 +38,8 @@ public class TransferClient {
|
||||||
private final EmitterProcessor<ItemUpdate<SupergroupInfo>> usefulSupergroups = EmitterProcessor.create();
|
private final EmitterProcessor<ItemUpdate<SupergroupInfo>> usefulSupergroups = EmitterProcessor.create();
|
||||||
private final Scheduler scheduler;
|
private final Scheduler scheduler;
|
||||||
|
|
||||||
public TransferClient(AsyncTdEasy client) {
|
public TransferClient(String alias, AsyncTdEasy client) {
|
||||||
|
this.alias = alias;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
this.scheduler = Schedulers.boundedElastic();
|
this.scheduler = Schedulers.boundedElastic();
|
||||||
|
@ -81,12 +85,18 @@ public class TransferClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Void> onUpdateSupergroup(Supergroup supergroup) {
|
private Mono<Void> onUpdateSupergroup(Supergroup supergroup) {
|
||||||
if (supergroupInfos.put(supergroup.id, supergroup) == null) {
|
// Fast checks to ignore most unwanted infos
|
||||||
return this
|
if (!supergroup.isChannel
|
||||||
.<SupergroupFullInfo>send(new GetSupergroupFullInfo(supergroup.id))
|
&&
|
||||||
.filter(TdResult::succeeded)
|
(supergroup.status.getConstructor() == ChatMemberStatusAdministrator.CONSTRUCTOR
|
||||||
.map(TdResult::result)
|
|| supergroup.status.getConstructor() == ChatMemberStatusCreator.CONSTRUCTOR)) {
|
||||||
.flatMap(supergroupFullInfo -> onUpdateSupergroupFullInfo(supergroup.id, supergroupFullInfo));
|
if (supergroupInfos.put(supergroup.id, supergroup) == null) {
|
||||||
|
return this
|
||||||
|
.<SupergroupFullInfo>send(new GetSupergroupFullInfo(supergroup.id))
|
||||||
|
.filter(TdResult::succeeded)
|
||||||
|
.map(TdResult::result)
|
||||||
|
.flatMap(supergroupFullInfo -> onUpdateSupergroupFullInfo(supergroup.id, supergroupFullInfo));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
@ -129,17 +139,26 @@ public class TransferClient {
|
||||||
if (chatInfo == null || baseInfo == null) {
|
if (chatInfo == null || baseInfo == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
} else {
|
} else {
|
||||||
if (baseInfo.status.getConstructor() != ChatMemberStatusAdministrator.CONSTRUCTOR) {
|
var baseChatInfo = new BaseChatInfo(id, chatInfo.title);
|
||||||
|
if (baseInfo.status.getConstructor() == ChatMemberStatusCreator.CONSTRUCTOR) {
|
||||||
|
var sgInfo = new SupergroupInfo(baseChatInfo, true, true);
|
||||||
|
return Optional.of(sgInfo);
|
||||||
|
} else if (baseInfo.status.getConstructor() == ChatMemberStatusAdministrator.CONSTRUCTOR) {
|
||||||
|
var adminStatus = (ChatMemberStatusAdministrator) baseInfo.status;
|
||||||
|
var sgInfo = new SupergroupInfo(baseChatInfo, adminStatus.canRestrictMembers, adminStatus.canInviteUsers);
|
||||||
|
return Optional.of(sgInfo);
|
||||||
|
} else {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
var adminStatus = (ChatMemberStatusAdministrator) baseInfo.status;
|
|
||||||
var baseChatInfo = new BaseChatInfo(id, chatInfo.title);
|
|
||||||
var sgInfo = new SupergroupInfo(baseChatInfo, adminStatus.canRestrictMembers, adminStatus.canInviteUsers);
|
|
||||||
return Optional.of(sgInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Flux<ItemUpdate<SupergroupInfo>> subscribeAdminSupergroups() {
|
public Flux<ItemUpdate<SupergroupInfo>> subscribeAdminSupergroups() {
|
||||||
return usefulSupergroups.hide();
|
return usefulSupergroups.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,5 +30,9 @@ public interface TransferService {
|
||||||
|
|
||||||
Flux<ItemUpdate<SupergroupInfo>> subscribeAdminSupergroups();
|
Flux<ItemUpdate<SupergroupInfo>> subscribeAdminSupergroups();
|
||||||
|
|
||||||
Mono<Void> transferMembers(BaseChatInfo sourceGroup, BaseChatInfo destGroup, Function<UserStatus, Mono<Void>> userStatusConsumer);
|
Mono<Void> transferMembers(BaseChatInfo sourceGroup,
|
||||||
|
BaseChatInfo destGroup,
|
||||||
|
Function<UserStatus, Mono<Void>> userStatusConsumer,
|
||||||
|
Function<Integer, Mono<Void>> percentageConsumer,
|
||||||
|
Function<String, Mono<Void>> phaseDescriptionConsumer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,41 +2,48 @@ package it.cavallium;
|
||||||
|
|
||||||
import static it.cavallium.PrimaryController.getUserbotPhoneNumber;
|
import static it.cavallium.PrimaryController.getUserbotPhoneNumber;
|
||||||
|
|
||||||
import com.google.common.collect.ConcurrentHashMultiset;
|
|
||||||
import com.google.i18n.phonenumbers.NumberParseException;
|
import com.google.i18n.phonenumbers.NumberParseException;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
|
||||||
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
|
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
|
||||||
|
import com.hazelcast.cp.internal.util.Tuple2;
|
||||||
|
import com.hazelcast.cp.internal.util.Tuple3;
|
||||||
import it.tdlight.jni.TdApi;
|
import it.tdlight.jni.TdApi;
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateClosed;
|
import it.tdlight.jni.TdApi.AuthorizationStateClosed;
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateClosing;
|
import it.tdlight.jni.TdApi.AuthorizationStateClosing;
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateLoggingOut;
|
import it.tdlight.jni.TdApi.AuthorizationStateLoggingOut;
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
||||||
|
import it.tdlight.jni.TdApi.ChatMemberStatusAdministrator;
|
||||||
|
import it.tdlight.jni.TdApi.ChatMemberStatusCreator;
|
||||||
|
import it.tdlight.jni.TdApi.GetUserPrivacySettingRules;
|
||||||
|
import it.tdlight.jni.TdApi.Supergroup;
|
||||||
|
import it.tdlight.jni.TdApi.SupergroupFullInfo;
|
||||||
import it.tdlight.jni.TdApi.Update;
|
import it.tdlight.jni.TdApi.Update;
|
||||||
import it.tdlight.jni.TdApi.UpdateSupergroup;
|
import it.tdlight.jni.TdApi.User;
|
||||||
|
import it.tdlight.jni.TdApi.UserFullInfo;
|
||||||
|
import it.tdlight.jni.TdApi.UserTypeRegular;
|
||||||
import it.tdlight.tdlibsession.td.easy.AsyncTdEasy;
|
import it.tdlight.tdlibsession.td.easy.AsyncTdEasy;
|
||||||
import it.tdlight.tdlibsession.td.easy.ParameterInfoPasswordHint;
|
import it.tdlight.tdlibsession.td.easy.ParameterInfoPasswordHint;
|
||||||
import it.tdlight.tdlibsession.td.easy.TdEasySettings;
|
import it.tdlight.tdlibsession.td.easy.TdEasySettings;
|
||||||
import it.tdlight.tdlibsession.td.middle.TdClusterManager;
|
import it.tdlight.tdlibsession.td.middle.TdClusterManager;
|
||||||
import it.tdlight.tdlibsession.td.middle.direct.AsyncTdMiddleDirect;
|
import it.tdlight.tdlibsession.td.middle.direct.AsyncTdMiddleDirect;
|
||||||
import it.tdlight.utils.MonoUtils;
|
import it.tdlight.utils.MonoUtils;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectSets;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.slf4j.event.Level;
|
||||||
import org.reactivestreams.Publisher;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import reactor.core.publisher.EmitterProcessor;
|
import reactor.core.publisher.EmitterProcessor;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.publisher.ReplayProcessor;
|
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
|
@ -177,8 +184,28 @@ public class TransferServiceImpl implements TransferService {
|
||||||
.then();
|
.then();
|
||||||
}))
|
}))
|
||||||
.doOnSuccess((_v) -> {
|
.doOnSuccess((_v) -> {
|
||||||
var newClient = new TransferClient(client);
|
var newClient = new TransferClient(alias, client);
|
||||||
clients.put(phoneNumberLong, newClient);
|
clients.put(phoneNumberLong, newClient);
|
||||||
|
|
||||||
|
newClient.subscribeAdminSupergroups().doOnNext(supergroupInfoItemUpdate -> {
|
||||||
|
if (supergroupInfoItemUpdate.isRemoved()) {
|
||||||
|
supergroupClients.compute(supergroupInfoItemUpdate.getItem().getBaseChatInfo().getSupergroupIdInt(), (sgId, clients) -> {
|
||||||
|
if (clients != null) {
|
||||||
|
clients.remove(newClient);
|
||||||
|
}
|
||||||
|
return clients;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
supergroupClients.compute(supergroupInfoItemUpdate.getItem().getBaseChatInfo().getSupergroupIdInt(), (sgId, clients) -> {
|
||||||
|
if (clients == null) {
|
||||||
|
clients = ObjectSets.synchronize(new ObjectOpenHashSet<>());
|
||||||
|
}
|
||||||
|
clients.add(newClient);
|
||||||
|
return clients;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).subscribe();
|
||||||
|
|
||||||
newClients.onNext(new ItemUpdate<>(false, newClient));
|
newClients.onNext(new ItemUpdate<>(false, newClient));
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
|
@ -239,23 +266,222 @@ public class TransferServiceImpl implements TransferService {
|
||||||
.fromStream(clients.values().stream().map(client -> new ItemUpdate<>(false, client))))
|
.fromStream(clients.values().stream().map(client -> new ItemUpdate<>(false, client))))
|
||||||
.filter(itemClient -> !itemClient.isRemoved())
|
.filter(itemClient -> !itemClient.isRemoved())
|
||||||
.map(ItemUpdate::getItem)
|
.map(ItemUpdate::getItem)
|
||||||
.map(client1 -> client1.subscribeAdminSupergroups().doOnNext(supergroupInfoItemUpdate -> {
|
.map(TransferClient::subscribeAdminSupergroups)
|
||||||
supergroupClients.compute(supergroupInfoItemUpdate.getItem().getBaseChatInfo().getSupergroupIdInt(), (sgId, clients) -> {
|
|
||||||
if (clients == null) {
|
|
||||||
clients = new HashSet<>();
|
|
||||||
}
|
|
||||||
clients.add(client1);
|
|
||||||
return clients;
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.flatMap(f -> f);
|
.flatMap(f -> f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> transferMembers(BaseChatInfo sourceGroup,
|
public Mono<Void> transferMembers(BaseChatInfo sourceGroup,
|
||||||
BaseChatInfo destGroup,
|
BaseChatInfo destGroup,
|
||||||
Function<UserStatus, Mono<Void>> userStatusConsumer) {
|
Function<UserStatus, Mono<Void>> userStatusConsumer,
|
||||||
return Mono.empty();
|
Function<Integer, Mono<Void>> percentageConsumer,
|
||||||
|
Function<String, Mono<Void>> phaseDescriptionConsumer) {
|
||||||
|
var sourceSupergroupClients = this.supergroupClients
|
||||||
|
.getOrDefault(sourceGroup.getSupergroupIdInt(), Set.of())
|
||||||
|
.stream()
|
||||||
|
.filter(clients::containsValue)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (sourceSupergroupClients.isEmpty()) {
|
||||||
|
return Mono.error(new Exception("No userbot can remove members from the source group"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var destSupergroupClients = this.supergroupClients
|
||||||
|
.getOrDefault(destGroup.getSupergroupIdInt(), Set.of())
|
||||||
|
.stream()
|
||||||
|
.filter(clients::containsValue)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (destSupergroupClients.isEmpty()) {
|
||||||
|
return Mono.error(new Exception("No userbot can add members to the destination group"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return percentageConsumer
|
||||||
|
.apply(0)
|
||||||
|
.then(phaseDescriptionConsumer.apply("Transfer from " + sourceGroup.getTitle() + " to " + destGroup.getTitle()))
|
||||||
|
|
||||||
|
// Check and get the set of userbots that can transfer users from group X to group Y
|
||||||
|
.then(phaseDescriptionConsumer.apply("Checking available userbots for removing users in the source group"))
|
||||||
|
.thenMany(Flux.fromIterable(sourceSupergroupClients))
|
||||||
|
.flatMap(client -> client
|
||||||
|
.send(new TdApi.GetMe())
|
||||||
|
.timeout(Duration.ofSeconds(5))
|
||||||
|
.then(client.<SupergroupFullInfo>send(new TdApi.GetSupergroupFullInfo(sourceGroup.getSupergroupIdInt())))
|
||||||
|
.timeout(Duration.ofSeconds(5))
|
||||||
|
.then(client.<Supergroup>send(new TdApi.GetSupergroup(sourceGroup.getSupergroupIdInt())))
|
||||||
|
.timeout(Duration.ofSeconds(5))
|
||||||
|
.filter(sourceGroupFullInfo -> {
|
||||||
|
if (sourceGroupFullInfo.succeeded()) {
|
||||||
|
if (sourceGroupFullInfo.result().status.getConstructor() == ChatMemberStatusAdministrator.CONSTRUCTOR) {
|
||||||
|
var statusAdmin = (ChatMemberStatusAdministrator) sourceGroupFullInfo.result().status;
|
||||||
|
if (statusAdmin.canRestrictMembers) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: Can't restrict members of group " + sourceGroup.getTitle());
|
||||||
|
}
|
||||||
|
} else if (sourceGroupFullInfo.result().status.getConstructor() == ChatMemberStatusCreator.CONSTRUCTOR) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: Can't administer group " + sourceGroup.getTitle());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: " + sourceGroupFullInfo.cause());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.map(_v -> client)
|
||||||
|
.onErrorResume(e -> {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: " + e.getLocalizedMessage());
|
||||||
|
return Mono.empty();
|
||||||
|
}))
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
.flatMap(transferSourceClients -> {
|
||||||
|
return phaseDescriptionConsumer.apply("Checking available userbots for adding users in the destination group")
|
||||||
|
.thenMany(Flux.fromIterable(destSupergroupClients))
|
||||||
|
.flatMap(client -> client
|
||||||
|
.send(new TdApi.GetMe())
|
||||||
|
.timeout(Duration.ofSeconds(5))
|
||||||
|
.then(client.<Supergroup>send(new TdApi.GetSupergroup(destGroup.getSupergroupIdInt())))
|
||||||
|
.timeout(Duration.ofSeconds(5))
|
||||||
|
.filter(destGroupFullInfo -> {
|
||||||
|
if (destGroupFullInfo.succeeded()) {
|
||||||
|
if (destGroupFullInfo.result().status.getConstructor() == ChatMemberStatusAdministrator.CONSTRUCTOR) {
|
||||||
|
var statusAdmin = (ChatMemberStatusAdministrator) destGroupFullInfo.result().status;
|
||||||
|
if (statusAdmin.canInviteUsers) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: Can't invite members to group " + destGroup.getTitle());
|
||||||
|
}
|
||||||
|
} else if (destGroupFullInfo.result().status.getConstructor() == ChatMemberStatusCreator.CONSTRUCTOR) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: Can't administer group " + destGroup.getTitle());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: " + destGroupFullInfo.cause());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.map(_v -> client)
|
||||||
|
.onErrorResume(e -> {
|
||||||
|
App.getLogService().append(Level.WARN, "Userbot " + client + " failed: " + e.getLocalizedMessage());
|
||||||
|
return Mono.empty();
|
||||||
|
}))
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
.map(transferDestClients -> Tuple2.of(transferSourceClients, transferDestClients));
|
||||||
|
})
|
||||||
|
.map(clientsTuple -> {
|
||||||
|
var sourceClients = clientsTuple.element1;
|
||||||
|
var destClients = clientsTuple.element2;
|
||||||
|
App.getLogService().append(Level.INFO, "Found source userbots: " + sourceClients.stream().map(TransferClient::toString).collect(Collectors.joining(", ")));
|
||||||
|
App.getLogService().append(Level.INFO, "Found destination userbots: " + destClients.stream().map(TransferClient::toString).collect(Collectors.joining(", ")));
|
||||||
|
var chosenClients = new HashSet<TransferClient>(sourceClients);
|
||||||
|
chosenClients.retainAll(destClients);
|
||||||
|
return chosenClients;
|
||||||
|
})
|
||||||
|
.filter(chosenClients -> !chosenClients.isEmpty())
|
||||||
|
.doOnNext(chosenClients -> {
|
||||||
|
App.getLogService().append(Level.INFO, "Chosen userbots: " + chosenClients.stream().map(TransferClient::toString).collect(Collectors.joining(", ")));
|
||||||
|
})
|
||||||
|
.switchIfEmpty(Mono.defer(() -> {
|
||||||
|
App.getLogService().append(Level.ERROR, "No userbots are admin in both groups!");
|
||||||
|
return Mono.error(new Exception("No userbots are admin in both groups!"));
|
||||||
|
}))
|
||||||
|
// Now we have a set of userbots that can transfer the users
|
||||||
|
|
||||||
|
// Get the list of members of the first group from a bot
|
||||||
|
.flatMap(clients -> {
|
||||||
|
return phaseDescriptionConsumer.apply("Obtaining group members")
|
||||||
|
.then(percentageConsumer.apply(5)).thenReturn(clients);
|
||||||
|
}).flatMap(clients -> {
|
||||||
|
// Get the first userbot
|
||||||
|
var client = clients.stream().findAny().orElseThrow(() -> new NullPointerException("No userbots found"));
|
||||||
|
|
||||||
|
// Get the members of the source group
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
var members = TransferUtils.getSupergroupMembers(client, sourceGroup.getSupergroupIdInt());
|
||||||
|
App.getLogService().append(Level.INFO, "Source group has " + members.size() + " members.");
|
||||||
|
return members;
|
||||||
|
}).subscribeOn(Schedulers.boundedElastic()).map(members -> Tuple3.of(client, clients, members));
|
||||||
|
})
|
||||||
|
// Finished getting the list of members of the source group
|
||||||
|
|
||||||
|
// Resolve users
|
||||||
|
.flatMap(context -> {
|
||||||
|
return phaseDescriptionConsumer.apply("Resolving users")
|
||||||
|
.then(percentageConsumer.apply(10)).thenReturn(context);
|
||||||
|
})
|
||||||
|
.flatMap(context -> {
|
||||||
|
var client = context.element1;
|
||||||
|
var clients = context.element2;
|
||||||
|
var unresolvedUsers = context.element3;
|
||||||
|
return Flux
|
||||||
|
.fromIterable(unresolvedUsers)
|
||||||
|
.flatMap(userId -> client.<User>send(new TdApi.GetUser(userId)))
|
||||||
|
.timeout(Duration.ofMinutes(2))
|
||||||
|
.flatMap(MonoFxUtils::orElseLogSkipError)
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
.map(resolvedUsers -> Tuple3.of(client, clients, resolvedUsers));
|
||||||
|
})
|
||||||
|
// Finished resolving users
|
||||||
|
|
||||||
|
// Filter out unsuitable users
|
||||||
|
.flatMap(context -> {
|
||||||
|
return phaseDescriptionConsumer.apply("Filtering users")
|
||||||
|
.then(percentageConsumer.apply(15)).thenReturn(context);
|
||||||
|
})
|
||||||
|
.flatMap(context -> {
|
||||||
|
var client = context.element1;
|
||||||
|
var clients = context.element2;
|
||||||
|
var unfilteredUsers = context.element3;
|
||||||
|
return Flux
|
||||||
|
.fromIterable(unfilteredUsers)
|
||||||
|
.filter(user -> {
|
||||||
|
if (user.haveAccess) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
userStatusConsumer.apply(new UserStatus(getName(user), user.id, UserStatusType.NO_ACCESS_HASH, ""));
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.filter(user -> {
|
||||||
|
if (user.type.getConstructor() == UserTypeRegular.CONSTRUCTOR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
userStatusConsumer.apply(new UserStatus(getName(user), user.id, UserStatusType.NOT_REGULAR_USER, ""));
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.filter(user -> {
|
||||||
|
if (!user.isScam) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
userStatusConsumer.apply(new UserStatus(getName(user), user.id, UserStatusType.SCAM_USER, ""));
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.filter(user -> {
|
||||||
|
if (user.restrictionReason == null || user.restrictionReason.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
userStatusConsumer.apply(new UserStatus(getName(user), user.id, UserStatusType.RESTRICTED_USER, "Restricted user: " + user.restrictionReason));
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.flatMap(user -> {
|
||||||
|
return userStatusConsumer
|
||||||
|
.apply(new UserStatus(getName(user), user.id, UserStatusType.JUST_FOUND, ""))
|
||||||
|
.thenReturn(user);
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMinutes(2))
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
.map(resolvedUsers -> Tuple3.of(client, clients, resolvedUsers));
|
||||||
|
})
|
||||||
|
// Finished filtering unsuitable users
|
||||||
|
|
||||||
|
|
||||||
|
.then(percentageConsumer.apply(100))
|
||||||
|
.then(phaseDescriptionConsumer.apply("Done"))
|
||||||
|
.then(Mono.delay(Duration.ofMillis(500)))
|
||||||
|
.then();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getName(User user) {
|
||||||
|
return String.join(" ", List.of("" + user.id, user.firstName, user.lastName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -263,7 +489,6 @@ public class TransferServiceImpl implements TransferService {
|
||||||
return Flux
|
return Flux
|
||||||
.fromIterable(clients.values())
|
.fromIterable(clients.values())
|
||||||
.flatMap(client -> client.send(new TdApi.Close()))
|
.flatMap(client -> client.send(new TdApi.Close()))
|
||||||
.log()
|
|
||||||
.collectList()
|
.collectList()
|
||||||
.then();
|
.then();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,26 @@
|
||||||
package it.cavallium;
|
package it.cavallium;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi;
|
||||||
import it.tdlight.jni.TdApi.Chat;
|
import it.tdlight.jni.TdApi.Chat;
|
||||||
import it.tdlight.jni.TdApi.ChatListMain;
|
import it.tdlight.jni.TdApi.ChatListMain;
|
||||||
|
import it.tdlight.jni.TdApi.ChatMember;
|
||||||
|
import it.tdlight.jni.TdApi.ChatMemberStatus;
|
||||||
|
import it.tdlight.jni.TdApi.ChatMembers;
|
||||||
import it.tdlight.jni.TdApi.ChatPosition;
|
import it.tdlight.jni.TdApi.ChatPosition;
|
||||||
import it.tdlight.jni.TdApi.Chats;
|
import it.tdlight.jni.TdApi.Chats;
|
||||||
import it.tdlight.jni.TdApi.GetChat;
|
import it.tdlight.jni.TdApi.GetChat;
|
||||||
import it.tdlight.jni.TdApi.GetChats;
|
import it.tdlight.jni.TdApi.GetChats;
|
||||||
|
import it.tdlight.jni.TdApi.GetSupergroupMembers;
|
||||||
|
import it.tdlight.jni.TdApi.SupergroupFullInfo;
|
||||||
import it.tdlight.utils.MonoUtils;
|
import it.tdlight.utils.MonoUtils;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
@ -142,6 +148,9 @@ public class TransferUtils {
|
||||||
.subscriberContext(context);
|
.subscriberContext(context);
|
||||||
})
|
})
|
||||||
.repeatWhen(nFlux -> nFlux.takeWhile(n -> n > 0))
|
.repeatWhen(nFlux -> nFlux.takeWhile(n -> n > 0))
|
||||||
|
.doOnNext(chats -> {
|
||||||
|
App.getLogService().append(Level.DEBUG, "Received " + chats.size() + " home chats");
|
||||||
|
})
|
||||||
.flatMap(Flux::fromIterable)
|
.flatMap(Flux::fromIterable)
|
||||||
.subscriberContext(ctx -> ctx.put("offsets",
|
.subscriberContext(ctx -> ctx.put("offsets",
|
||||||
new AtomicReference<>(new ChatIdAndOrderOffsets(0L, 9223372036854775807L))
|
new AtomicReference<>(new ChatIdAndOrderOffsets(0L, 9223372036854775807L))
|
||||||
|
@ -157,4 +166,86 @@ public class TransferUtils {
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
.<Chat>send(new TdApi.GetChat(supergroupId))
|
||||||
|
.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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,42 @@
|
||||||
package it.cavallium;
|
package it.cavallium;
|
||||||
|
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
|
||||||
public class UserStatus {
|
public class UserStatus {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int id;
|
private final int id;
|
||||||
private final UserStatusType statusType;
|
private final UserStatusType statusType;
|
||||||
private final String errorDescription;
|
private final String errorDescription;
|
||||||
|
private final SimpleStringProperty user = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty status = new SimpleStringProperty("");
|
||||||
|
|
||||||
public UserStatus(String name, int id, UserStatusType statusType, String errorDescription) {
|
public UserStatus(String name, int id, UserStatusType statusType, String errorDescription) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.statusType = statusType;
|
this.statusType = statusType;
|
||||||
this.errorDescription = errorDescription;
|
this.errorDescription = errorDescription;
|
||||||
|
this.user.set(name);
|
||||||
|
this.status.set(errorDescription.isBlank() ? translateStatusType(statusType) : errorDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String translateStatusType(UserStatusType statusType) {
|
||||||
|
switch (statusType) {
|
||||||
|
case SCAM_USER:
|
||||||
|
return "Scam user, skipped";
|
||||||
|
case NO_ACCESS_HASH:
|
||||||
|
return "No access hash, skipped";
|
||||||
|
case RESTRICTED_USER:
|
||||||
|
return "User restricted, skipped";
|
||||||
|
case JUST_FOUND:
|
||||||
|
return "";
|
||||||
|
case UNKNOWN:
|
||||||
|
return "Unknown";
|
||||||
|
case NOT_REGULAR_USER:
|
||||||
|
return "Not a regular user, skipped";
|
||||||
|
default:
|
||||||
|
return statusType.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
|
@ -31,6 +55,14 @@ public class UserStatus {
|
||||||
return statusType;
|
return statusType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUser() {
|
||||||
|
return user.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
|
@ -45,21 +77,13 @@ public class UserStatus {
|
||||||
if (id != that.id) {
|
if (id != that.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (name != null ? !name.equals(that.name) : that.name != null) {
|
return true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (statusType != that.statusType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return errorDescription != null ? errorDescription.equals(that.errorDescription) : that.errorDescription == null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = name != null ? name.hashCode() : 0;
|
int result = name != null ? name.hashCode() : 0;
|
||||||
result = 31 * result + id;
|
result = 31 * result + id;
|
||||||
result = 31 * result + (statusType != null ? statusType.hashCode() : 0);
|
|
||||||
result = 31 * result + (errorDescription != null ? errorDescription.hashCode() : 0);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package it.cavallium;
|
package it.cavallium;
|
||||||
|
|
||||||
public enum UserStatusType {
|
public enum UserStatusType {
|
||||||
UNKNOWN
|
NO_ACCESS_HASH, NOT_REGULAR_USER, SCAM_USER, RESTRICTED_USER, JUST_FOUND, UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<?import javafx.scene.control.TableView?>
|
<?import javafx.scene.control.TableView?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
<?import javafx.scene.control.ToolBar?>
|
<?import javafx.scene.control.ToolBar?>
|
||||||
|
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||||
<?import javafx.scene.layout.BorderPane?>
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
<?import javafx.scene.layout.GridPane?>
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
@ -38,7 +39,7 @@
|
||||||
</Menu>
|
</Menu>
|
||||||
</menus>
|
</menus>
|
||||||
</MenuBar>
|
</MenuBar>
|
||||||
<SplitPane dividerPositions="0.8237442922374429" orientation="VERTICAL">
|
<SplitPane dividerPositions="0.8237442922374429" orientation="VERTICAL" prefHeight="9999.0">
|
||||||
<items>
|
<items>
|
||||||
<SplitPane dividerPositions="0.5" focusTraversable="true" prefHeight="-1.0" prefWidth="-1.0">
|
<SplitPane dividerPositions="0.5" focusTraversable="true" prefHeight="-1.0" prefWidth="-1.0">
|
||||||
<items>
|
<items>
|
||||||
|
@ -90,7 +91,6 @@
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints />
|
<RowConstraints />
|
||||||
<RowConstraints vgrow="SOMETIMES" />
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<RowConstraints vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<VBox maxHeight="-Infinity" minHeight="-Infinity" spacing="10.0" GridPane.columnSpan="2">
|
<VBox maxHeight="-Infinity" minHeight="-Infinity" spacing="10.0" GridPane.columnSpan="2">
|
||||||
|
@ -105,8 +105,16 @@
|
||||||
</VBox>
|
</VBox>
|
||||||
<TableView fx:id="statusTable" GridPane.columnSpan="2" GridPane.rowIndex="1">
|
<TableView fx:id="statusTable" GridPane.columnSpan="2" GridPane.rowIndex="1">
|
||||||
<columns>
|
<columns>
|
||||||
<TableColumn minWidth="100.0" prefWidth="200.0" text="User" />
|
<TableColumn minWidth="100.0" prefWidth="200.0" text="User">
|
||||||
<TableColumn minWidth="100.0" prefWidth="100.0" text="Status" />
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="user" />
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="100.0" prefWidth="100.0" text="Status">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="status" />
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
</columns>
|
</columns>
|
||||||
<columnResizePolicy>
|
<columnResizePolicy>
|
||||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||||
|
@ -127,7 +135,7 @@
|
||||||
</Text>
|
</Text>
|
||||||
</top>
|
</top>
|
||||||
<center>
|
<center>
|
||||||
<ListView fx:id="log" style="-fx-font-size: 8" BorderPane.alignment="CENTER" />
|
<ListView fx:id="log" style="-fx-font-size: 10" BorderPane.alignment="CENTER" />
|
||||||
</center>
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</items>
|
</items>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 85bac8670d201922ac695f3f086eb0c6cd526e8f
|
Subproject commit 6fd5b099c3f90e1dbca28d37c9003541e47b4d51
|
Loading…
Reference in New Issue
Block a user