Add chat and chats command
This commit is contained in:
parent
917f498034
commit
6d002239c2
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
jelegram.iml
|
||||
/target/
|
||||
/.cache/
|
||||
/run/*
|
||||
|
|
102
pom.xml
102
pom.xml
|
@ -36,7 +36,7 @@
|
|||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-java-bom</artifactId>
|
||||
<version>2.8.9.0</version>
|
||||
<version>2.8.9.2</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
@ -65,6 +65,18 @@
|
|||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives-linux-amd64</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives-windows-amd64</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives-linux-aarch64</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives-osx-amd64</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>info.picocli</groupId>
|
||||
<artifactId>picocli-shell-jline3</artifactId>
|
||||
|
@ -75,6 +87,21 @@
|
|||
<artifactId>jansi</artifactId>
|
||||
<version>2.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.14.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||
<version>2.19.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -97,7 +124,7 @@
|
|||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.4.2</version>
|
||||
|
@ -122,6 +149,77 @@
|
|||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
|
||||
<!-- note that the main class is set *here* -->
|
||||
|
||||
<mainClass>picocli.shell.jline3.example.Example</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- now make the jar chmod +x style executable -->
|
||||
<plugin>
|
||||
<groupId>org.skife.maven</groupId>
|
||||
<artifactId>really-executable-jar-maven-plugin</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<configuration>
|
||||
<!-- value of flags will be interpolated into the java invocation -->
|
||||
<!-- as "java $flags -jar ..." -->
|
||||
<flags>-Xmx256M</flags>
|
||||
|
||||
<!-- (optional) name for binary executable, if not set will just -->
|
||||
<!-- make the regular jar artifact executable -->
|
||||
<programFile>jelegram</programFile>
|
||||
|
||||
<!-- (optional) support other packaging formats than jar -->
|
||||
<!-- <allowOtherTypes>true</allowOtherTypes> -->
|
||||
|
||||
<!-- (optional) name for a file that will define what script gets -->
|
||||
<!-- embedded into the executable jar. This can be used to -->
|
||||
<!-- override the default startup script which is -->
|
||||
<!-- `#!/bin/sh -->
|
||||
<!-- -->
|
||||
<!-- exec java " + flags + " -jar "$0" "$@" -->
|
||||
<!-- <scriptFile>src/packaging/someScript.extension</scriptFile> -->
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>really-executable-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
|
|
@ -5,6 +5,10 @@ module jelegram {
|
|||
requires tdlight.api;
|
||||
requires tdlight.java;
|
||||
requires com.google.zxing;
|
||||
requires com.fasterxml.jackson.core;
|
||||
requires com.fasterxml.jackson.annotation;
|
||||
requires com.fasterxml.jackson.databind;
|
||||
requires it.unimi.dsi.fastutil.core;
|
||||
|
||||
exports picocli.shell.jline3;
|
||||
opens picocli.shell.jline3.example;
|
||||
|
|
3
src/main/java/picocli/shell/jline3/example/ChatData.java
Normal file
3
src/main/java/picocli/shell/jline3/example/ChatData.java
Normal file
|
@ -0,0 +1,3 @@
|
|||
package picocli.shell.jline3.example;
|
||||
|
||||
public class ChatData {}
|
|
@ -0,0 +1,5 @@
|
|||
package picocli.shell.jline3.example;
|
||||
|
||||
public enum CliInputParameterValue {
|
||||
ASK_AUTH_DATA_TYPE
|
||||
}
|
|
@ -4,5 +4,10 @@ import it.tdlight.client.InputParameter;
|
|||
import it.tdlight.client.ParameterInfo;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public record ClientInteractionRequest(TDLibInstance instance, InputParameter parameter, ParameterInfo parameterInfo,
|
||||
Consumer<String> result) {}
|
||||
public record ClientInteractionRequest(TDLibInstance instance, CustomInputParameter parameter, ParameterInfo parameterInfo,
|
||||
Consumer<String> result) {
|
||||
sealed interface CustomInputParameter {
|
||||
record StandardInputParameter(InputParameter value) implements CustomInputParameter {}
|
||||
record CliInputParameter(CliInputParameterValue value) implements CustomInputParameter {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,43 @@
|
|||
package picocli.shell.jline3.example;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import it.tdlight.client.APIToken;
|
||||
import it.tdlight.client.AuthenticationData;
|
||||
import it.tdlight.client.ConsoleInteractiveAuthenticationData;
|
||||
import it.tdlight.client.ParameterInfoCode;
|
||||
import it.tdlight.client.ParameterInfoNotifyLink;
|
||||
import it.tdlight.client.ParameterInfoPasswordHint;
|
||||
import it.tdlight.client.ParameterInfoTermsOfService;
|
||||
import it.tdlight.common.internal.InternalClientManager;
|
||||
import it.tdlight.common.utils.CantLoadLibrary;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.ChatList;
|
||||
import it.tdlight.jni.TdApi.ChatListArchive;
|
||||
import it.tdlight.jni.TdApi.ChatListMain;
|
||||
import it.tdlight.jni.TdApi.Chats;
|
||||
import it.tdlight.jni.TdApi.GetChat;
|
||||
import it.tdlight.jni.TdApi.LoadChats;
|
||||
import it.tdlight.jni.TdApi.Ok;
|
||||
import it.tdlight.jni.TdApi.TermsOfService;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -47,9 +62,12 @@ import picocli.CommandLine;
|
|||
import picocli.CommandLine.ArgGroup;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
import picocli.CommandLine.ParentCommand;
|
||||
import picocli.shell.jline3.PicocliCommands;
|
||||
import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
|
||||
import picocli.shell.jline3.example.ClientInteractionRequest.CustomInputParameter.CliInputParameter;
|
||||
import picocli.shell.jline3.example.ClientInteractionRequest.CustomInputParameter.StandardInputParameter;
|
||||
|
||||
/**
|
||||
* Example that demonstrates how to build an interactive shell with JLine3 and picocli.
|
||||
|
@ -60,6 +78,8 @@ import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
|
|||
*/
|
||||
public class Example {
|
||||
|
||||
static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
static String currentInstance = null;
|
||||
static boolean waitingLogin = false;
|
||||
static Map<String, TDLibInstance> instanceMap = new HashMap<>();
|
||||
|
@ -80,7 +100,9 @@ public class Example {
|
|||
"Hit @|magenta ALT-S|@ to toggle tailtips.",
|
||||
""},
|
||||
footer = {"", "Press Ctrl-D to exit."},
|
||||
subcommands = {InstanceCommand.class, PicocliCommands.ClearScreen.class, CommandLine.HelpCommand.class})
|
||||
subcommands = {InstanceCommand.class, ChatsCommand.class,
|
||||
ChatCommand.class, PicocliCommands.ClearScreen.class,
|
||||
CommandLine.HelpCommand.class})
|
||||
static class CliCommands implements Runnable {
|
||||
PrintWriter out;
|
||||
|
||||
|
@ -99,6 +121,8 @@ public class Example {
|
|||
description = {"Instance-related commands."},
|
||||
subcommands = {ConfigureCommand.class, StartCommand.class, StopCommand.class, CommandLine.HelpCommand.class})
|
||||
static class InstanceCommand implements Runnable {
|
||||
|
||||
private static final APIToken API_TOKEN = APIToken.example();
|
||||
@Option(names = {"-v", "--verbose"},
|
||||
description = { "Specify multiple -v options to increase verbosity.",
|
||||
"For example, `-v -v -v` or `-vvv`"})
|
||||
|
@ -120,20 +144,74 @@ public class Example {
|
|||
}
|
||||
}
|
||||
|
||||
@Command(mixinStandardHelpOptions = true, subcommands = {CommandLine.HelpCommand.class},
|
||||
description = "Add an instance.")
|
||||
public void add(@Option(names = {"-n", "--name"}, required = true) String name) throws CantLoadLibrary {
|
||||
@Command(mixinStandardHelpOptions = true, description = "Add an instance.")
|
||||
public void add(@Parameters(paramLabel = "<name>", description = "Instance name.", arity = "1") String name)
|
||||
throws CantLoadLibrary {
|
||||
if (instanceMap.containsKey(name)) {
|
||||
System.err.printf("Instance %s already exists%n", name);
|
||||
return;
|
||||
}
|
||||
currentInstance = name;
|
||||
instanceMap.put(name, new TDLibInstance(name, interactionRequests));
|
||||
instanceMap.put(name, new TDLibInstance(API_TOKEN, name, interactionRequests));
|
||||
}
|
||||
|
||||
@Command(mixinStandardHelpOptions = true, subcommands = {CommandLine.HelpCommand.class},
|
||||
description = "Select an instance.")
|
||||
public void select(@Option(names = {"-n", "--name"}, required = true) String name) {
|
||||
@Command(mixinStandardHelpOptions = true, description = "Load an instance from disk.")
|
||||
public void load(@Parameters(paramLabel = "<path>", description = "Instance path.", arity = "1") Path path)
|
||||
throws CantLoadLibrary, IOException {
|
||||
var settingsFilePath = path.resolve("settings.json");
|
||||
if (Files.notExists(settingsFilePath)) {
|
||||
System.err.printf("The following path does not exist: %s%n", settingsFilePath);
|
||||
return;
|
||||
}
|
||||
if (!Files.isRegularFile(settingsFilePath)) {
|
||||
System.err.printf("The following path is not valid: %s%n", settingsFilePath);
|
||||
return;
|
||||
}
|
||||
if (!Files.isReadable(settingsFilePath)) {
|
||||
System.err.printf("The following path is not readable: %s%n", settingsFilePath);
|
||||
return;
|
||||
}
|
||||
var settings = mapper.readValue(Files.readString(settingsFilePath, StandardCharsets.UTF_8), InstanceSettings.class);
|
||||
var name = settings.name;
|
||||
if (instanceMap.containsKey(name)) {
|
||||
System.err.printf("Instance %s already exists%n", name);
|
||||
return;
|
||||
}
|
||||
TDLibInstance instance = new TDLibInstance(API_TOKEN, settings, interactionRequests);
|
||||
currentInstance = name;
|
||||
instanceMap.put(name, instance);
|
||||
System.out.println("Client is loading...");
|
||||
waitingLogin = true;
|
||||
instance.start(settings.toAuthData());
|
||||
}
|
||||
|
||||
private void askAuthDataType(TDLibInstance instance) {
|
||||
interactionRequests.offer(new ClientInteractionRequest(instance,
|
||||
new CliInputParameter(CliInputParameterValue.ASK_AUTH_DATA_TYPE),
|
||||
null,
|
||||
result -> {
|
||||
AuthenticationData authenticationData;
|
||||
if (result.startsWith("phone ")) {
|
||||
authenticationData = AuthenticationData.user(Long.parseLong(result
|
||||
.substring("phone ".length())
|
||||
.replaceAll("[^\\d.]", "")));
|
||||
} else if (result.startsWith("token ")) {
|
||||
authenticationData = AuthenticationData.bot(result.substring("token ".length()));
|
||||
} else if (result.equals("qr")) {
|
||||
authenticationData = AuthenticationData.qrCode();
|
||||
} else {
|
||||
System.err.println("Invalid auth data type, please, enter a correctly formatted response.");
|
||||
askAuthDataType(instance);
|
||||
return;
|
||||
}
|
||||
instance.start(authenticationData);
|
||||
System.out.println("Client loaded.");
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@Command(mixinStandardHelpOptions = true, description = "Select an instance.")
|
||||
public void select(@Parameters(paramLabel = "<name>", description = "Instance name.", arity = "1") String name) {
|
||||
if (!instanceMap.containsKey(name)) {
|
||||
System.err.printf("Instance %s does not exist%n", name);
|
||||
return;
|
||||
|
@ -142,9 +220,171 @@ public class Example {
|
|||
}
|
||||
}
|
||||
|
||||
@Command(name = "configure", mixinStandardHelpOptions = true, version = "1.0",
|
||||
description = {"Instance-related configuration command."},
|
||||
@Command(name = "chats", mixinStandardHelpOptions = true, version = "1.0",
|
||||
description = {"Chats-related commands."},
|
||||
subcommands = {CommandLine.HelpCommand.class})
|
||||
static class ChatsCommand implements Runnable {
|
||||
|
||||
@Option(names = {"-v", "--verbose"},
|
||||
description = { "Specify multiple -v options to increase verbosity.",
|
||||
"For example, `-v -v -v` or `-vvv`"})
|
||||
private boolean[] verbosity = {};
|
||||
|
||||
@Option(names = {"-l", "--list"},
|
||||
description = { "Chats list. Can be \"main\" or \"archive\""})
|
||||
private String chatsList = "main";
|
||||
|
||||
@Option(names = {"-s", "--skip"},
|
||||
description = { "Skip the following chats."})
|
||||
private int skipCount = 0;
|
||||
|
||||
@Option(names = {"-c", "--count"},
|
||||
description = { "Show n chats."})
|
||||
private int count = 100;
|
||||
|
||||
@ParentCommand CliCommands parent;
|
||||
|
||||
public void run() {
|
||||
if (currentInstance == null) {
|
||||
System.out.println("No instance is active.");
|
||||
return;
|
||||
}
|
||||
var i = getInstance();
|
||||
ChatList chatList;
|
||||
if (chatsList.equals("main")) {
|
||||
chatList = new ChatListMain();
|
||||
} else if (chatsList.equals("archive")) {
|
||||
chatList = new ChatListArchive();
|
||||
} else {
|
||||
System.err.println("Invalid chat list: " + chatsList);
|
||||
return;
|
||||
}
|
||||
var chats = getChatsTo(i, chatList, skipCount + count);
|
||||
System.out.printf("Chat list %s has %d chats. (shown from %d to %d)%n",
|
||||
chatsList,
|
||||
chats.totalCount,
|
||||
Math.min(skipCount, chats.totalCount),
|
||||
Math.min(skipCount + count, chats.totalCount)
|
||||
);
|
||||
long[] chatIds = chats.chatIds;
|
||||
for (int j = skipCount; j < chatIds.length; j++) {
|
||||
long chatId = chatIds[j];
|
||||
var chat = i.send(new GetChat(chatId)).join();
|
||||
if (verbosity.length == 0) {
|
||||
System.out.printf("\t- %s%n", chat.title);
|
||||
} else {
|
||||
System.out.printf("\t- [%d] %s%n", chat.id, chat.title);
|
||||
}
|
||||
}
|
||||
var more = Math.max(0, chats.totalCount - (skipCount + count));
|
||||
if (more > 0) {
|
||||
System.out.printf(" ... and %d more, use the argument --skip=%d to see them.%n", more, skipCount + count);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getIsBot(TDLibInstance i) {
|
||||
return i.getAuthenticationData().isBot();
|
||||
}
|
||||
|
||||
private Chats getChatsTo(TDLibInstance instance, ChatList chatList, int max) {
|
||||
var bot = getIsBot(instance);
|
||||
if (bot && chatList.getConstructor() != ChatListMain.CONSTRUCTOR) {
|
||||
return new Chats(0, new long[0]);
|
||||
}
|
||||
if (bot) {
|
||||
var chats = instance.getChats();
|
||||
var array = new long[chats.size()];
|
||||
var result = new Chats(chats.size(), array);
|
||||
int i = 0;
|
||||
for (Long chat : chats) {
|
||||
array[i++] = chat;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int next = 0;
|
||||
while (next < max) {
|
||||
next += 100;
|
||||
instance.send(new LoadChats(chatList, next)).exceptionallyCompose(ex -> {
|
||||
if (ex instanceof TDException ex2 && ex2.code == 404) {
|
||||
return CompletableFuture.completedFuture(new Ok());
|
||||
} else {
|
||||
return CompletableFuture.failedFuture(ex);
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
return instance.send(new TdApi.GetChats(chatList, max)).join();
|
||||
}
|
||||
}
|
||||
|
||||
@Command(name = "chat", mixinStandardHelpOptions = true, version = "1.0",
|
||||
description = {"Chat-related commands."},
|
||||
subcommands = {CommandLine.HelpCommand.class})
|
||||
static class ChatCommand implements Runnable {
|
||||
|
||||
@Option(names = {"-v", "--verbose"},
|
||||
description = { "Specify multiple -v options to increase verbosity.",
|
||||
"For example, `-v -v -v` or `-vvv`"})
|
||||
private boolean[] verbosity = {};
|
||||
|
||||
@Parameters(paramLabel = "id", description = { "Chat id"}, arity = "1")
|
||||
private long id;
|
||||
|
||||
@ParentCommand CliCommands parent;
|
||||
|
||||
public void run() {
|
||||
if (currentInstance == null) {
|
||||
System.out.println("No instance is active.");
|
||||
return;
|
||||
}
|
||||
var i = getInstance();
|
||||
var chat = i.send(new TdApi.GetChat(id)).join();
|
||||
System.out.printf("Chat %s [%d]%n", chat.title, chat.id);
|
||||
var lastMessage = chat.lastMessage;
|
||||
if (lastMessage != null) {
|
||||
System.out.printf("Last message:%n");
|
||||
var sender = i.getSender(lastMessage.senderId).join();
|
||||
System.out.printf("\tSender: %s [%d]%n", sender.title, sender.id);
|
||||
var text = i.contentToString(lastMessage.content).join();
|
||||
System.out.printf("\t%s%n", text);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getIsBot(TDLibInstance i) {
|
||||
return i.getAuthenticationData().isBot();
|
||||
}
|
||||
|
||||
private Chats getChatsTo(TDLibInstance instance, ChatList chatList, int max) {
|
||||
var bot = getIsBot(instance);
|
||||
if (bot && chatList.getConstructor() != ChatListMain.CONSTRUCTOR) {
|
||||
return new Chats(0, new long[0]);
|
||||
}
|
||||
if (bot) {
|
||||
var chats = instance.getChats();
|
||||
var array = new long[chats.size()];
|
||||
var result = new Chats(chats.size(), array);
|
||||
int i = 0;
|
||||
for (Long chat : chats) {
|
||||
array[i++] = chat;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int next = 0;
|
||||
while (next < max) {
|
||||
next += 100;
|
||||
instance.send(new LoadChats(chatList, next)).exceptionallyCompose(ex -> {
|
||||
if (ex instanceof TDException ex2 && ex2.code == 404) {
|
||||
return CompletableFuture.completedFuture(new Ok());
|
||||
} else {
|
||||
return CompletableFuture.failedFuture(ex);
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
return instance.send(new TdApi.GetChats(chatList, max)).join();
|
||||
}
|
||||
}
|
||||
|
||||
@Command(name = "configure", mixinStandardHelpOptions = true, version = "1.0",
|
||||
description = {"Instance-related configuration command."})
|
||||
static class ConfigureCommand implements Runnable {
|
||||
@Option(names = {"-p", "--path"},
|
||||
description = { "Specify the path."})
|
||||
|
@ -162,7 +402,8 @@ public class Example {
|
|||
description = { "Use the chats database."}, negatable = true)
|
||||
private Boolean useTestDc;
|
||||
|
||||
@ParentCommand InstanceCommand parent;
|
||||
@ParentCommand
|
||||
InstanceCommand parent;
|
||||
|
||||
public void run() {
|
||||
if (currentInstance == null) {
|
||||
|
@ -172,7 +413,7 @@ public class Example {
|
|||
var instance = getInstance();
|
||||
var settings = instance.getSettings();
|
||||
if (path != null) {
|
||||
instance.setPath(currentInstance);
|
||||
instance.setPath(path);
|
||||
System.out.printf("Path set to \"%s\".%n", path);
|
||||
}
|
||||
if (useMessagesDb != null) {
|
||||
|
@ -194,9 +435,7 @@ public class Example {
|
|||
}
|
||||
}
|
||||
|
||||
@Command(name = "start", mixinStandardHelpOptions = true, version = "1.0",
|
||||
description = {"Start the instance."},
|
||||
subcommands = {CommandLine.HelpCommand.class})
|
||||
@Command(name = "start", mixinStandardHelpOptions = true, version = "1.0", description = {"Start the instance."})
|
||||
static class StartCommand implements Runnable {
|
||||
|
||||
@ParentCommand InstanceCommand parent;
|
||||
|
@ -222,6 +461,11 @@ public class Example {
|
|||
return;
|
||||
}
|
||||
var instance = getInstance();
|
||||
var path = instance.getPath();
|
||||
if (Files.exists(path)) {
|
||||
System.err.printf("Instance path already exists: %s%n", path);
|
||||
return;
|
||||
}
|
||||
AuthenticationData authenticationData;
|
||||
if (authMode.qrLogin) {
|
||||
authenticationData = AuthenticationData.qrCode();
|
||||
|
@ -239,9 +483,7 @@ public class Example {
|
|||
}
|
||||
}
|
||||
|
||||
@Command(name = "stop", mixinStandardHelpOptions = true, version = "1.0",
|
||||
description = {"Stop the instance."},
|
||||
subcommands = {CommandLine.HelpCommand.class})
|
||||
@Command(name = "stop", mixinStandardHelpOptions = true, version = "1.0", description = {"Stop the instance."})
|
||||
static class StopCommand implements Runnable {
|
||||
|
||||
@ParentCommand InstanceCommand parent;
|
||||
|
@ -299,7 +541,11 @@ public class Example {
|
|||
builtins.setLineReader(reader);
|
||||
commands.setReader(reader);
|
||||
factory.setTerminal(terminal);
|
||||
TailTipWidgets widgets = new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER);
|
||||
TailTipWidgets widgets = new TailTipWidgets(reader,
|
||||
systemRegistry::commandDescription,
|
||||
5,
|
||||
TailTipWidgets.TipType.COMPLETER
|
||||
);
|
||||
widgets.enable();
|
||||
KeyMap<Binding> keyMap = reader.getKeyMaps().get("main");
|
||||
keyMap.bind(new Reference("tailtip-toggle"), KeyMap.alt("s"));
|
||||
|
@ -309,6 +555,7 @@ public class Example {
|
|||
|
||||
// start the shell and process input until the user quits with Ctrl-D
|
||||
String line;
|
||||
shell:
|
||||
while (true) {
|
||||
try {
|
||||
systemRegistry.cleanUp();
|
||||
|
@ -326,13 +573,14 @@ public class Example {
|
|||
if (lastIr != null) {
|
||||
var requestAndResponse = getInteractionRequest(lastIr);
|
||||
prompt = requestAndResponse.getKey().orElse(null);
|
||||
if (prompt != null) {
|
||||
prompt += "\n> ";
|
||||
}
|
||||
line = reader.readLine(prompt, null, (MaskingCallback) null, null);
|
||||
if (requestAndResponse.getValue().isPresent()) {
|
||||
System.out.println(prompt);
|
||||
lastIr.result().accept(requestAndResponse.getValue().get());
|
||||
} else {
|
||||
if (prompt != null) {
|
||||
prompt += "\n> ";
|
||||
}
|
||||
line = reader.readLine(prompt, null, (MaskingCallback) null, null);
|
||||
lastIr.result().accept(line);
|
||||
}
|
||||
} else if (!waitingLogin) {
|
||||
|
@ -342,34 +590,38 @@ public class Example {
|
|||
} catch (UserInterruptException e) {
|
||||
// Ignore
|
||||
} catch (EndOfFileException e) {
|
||||
return;
|
||||
break shell;
|
||||
} catch (Exception e) {
|
||||
systemRegistry.trace(e);
|
||||
}
|
||||
}
|
||||
for (var entry : new HashSet<>(instanceMap.entrySet())) {
|
||||
try {
|
||||
entry.getValue().stop();
|
||||
} catch (Exception ex) {
|
||||
System.out.printf("Failed to close instance %s%n", entry.getKey());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
InternalClientManager.get("tdlight").close();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
AnsiConsole.systemUninstall();
|
||||
}
|
||||
}
|
||||
|
||||
private static Entry<Optional<String>, Optional<String>> getInteractionRequest(ClientInteractionRequest interactionRequest) {
|
||||
var parameter = interactionRequest.parameter();
|
||||
var customParameter = interactionRequest.parameter();
|
||||
var parameterInfo = interactionRequest.parameterInfo();
|
||||
var authenticationData = interactionRequest.instance().getAuthenticationData();
|
||||
var resultCons = interactionRequest.result();
|
||||
String who;
|
||||
boolean useRealWho;
|
||||
if (authenticationData instanceof ConsoleInteractiveAuthenticationData) {
|
||||
useRealWho = ((ConsoleInteractiveAuthenticationData) authenticationData).isInitialized();
|
||||
} else {
|
||||
useRealWho = true;
|
||||
}
|
||||
if (!useRealWho) {
|
||||
who = "login";
|
||||
} else if (authenticationData.isQrCode()) {
|
||||
if (authenticationData.isQrCode()) {
|
||||
who = "QR login";
|
||||
} else if (authenticationData.isBot()) {
|
||||
who = authenticationData.getBotToken().split(":", 2)[0];
|
||||
|
@ -378,74 +630,84 @@ public class Example {
|
|||
}
|
||||
String question;
|
||||
boolean trim = false;
|
||||
switch (parameter) {
|
||||
case ASK_FIRST_NAME:
|
||||
question = "Enter first name";
|
||||
trim = true;
|
||||
break;
|
||||
case ASK_LAST_NAME:
|
||||
question = "Enter last name";
|
||||
trim = true;
|
||||
break;
|
||||
case ASK_CODE:
|
||||
question = "Enter authentication code";
|
||||
ParameterInfoCode codeInfo = ((ParameterInfoCode) parameterInfo);
|
||||
question += "\n\tPhone number: " + codeInfo.getPhoneNumber();
|
||||
question += "\n\tTimeout: " + codeInfo.getTimeout() + " seconds";
|
||||
question += "\n\tCode type: " + codeInfo.getType().getClass().getSimpleName()
|
||||
.replace("AuthenticationCodeType", "");
|
||||
if (codeInfo.getNextType() != null) {
|
||||
question += "\n\tNext code type: " + codeInfo
|
||||
.getNextType()
|
||||
.getClass()
|
||||
.getSimpleName()
|
||||
.replace("AuthenticationCodeType", "");
|
||||
}
|
||||
trim = true;
|
||||
break;
|
||||
case ASK_PASSWORD:
|
||||
question = "Enter your password";
|
||||
String passwordMessage = "Password authorization:";
|
||||
String hint = ((ParameterInfoPasswordHint) parameterInfo).getHint();
|
||||
if (hint != null && !hint.isEmpty()) {
|
||||
passwordMessage += "\n\tHint: " + hint;
|
||||
}
|
||||
boolean hasRecoveryEmailAddress = ((ParameterInfoPasswordHint) parameterInfo)
|
||||
.hasRecoveryEmailAddress();
|
||||
passwordMessage += "\n\tHas recovery email: " + hasRecoveryEmailAddress;
|
||||
String recoveryEmailAddressPattern = ((ParameterInfoPasswordHint) parameterInfo)
|
||||
.getRecoveryEmailAddressPattern();
|
||||
if (recoveryEmailAddressPattern != null && !recoveryEmailAddressPattern.isEmpty()) {
|
||||
passwordMessage += "\n\tRecovery email address pattern: " + recoveryEmailAddressPattern;
|
||||
}
|
||||
System.out.println(passwordMessage);
|
||||
break;
|
||||
case NOTIFY_LINK:
|
||||
String link = ((ParameterInfoNotifyLink) parameterInfo).getLink();
|
||||
var sb = new StringBuilder();
|
||||
sb.append("Please confirm this login link on another device: " + link + "\n");
|
||||
sb.append("\n");
|
||||
sb.append(getQr(link) + "\n");
|
||||
sb.append("\n");
|
||||
return Map.entry(Optional.of(sb.toString()), Optional.of(""));
|
||||
case TERMS_OF_SERVICE:
|
||||
TermsOfService tos = ((ParameterInfoTermsOfService) parameterInfo).getTermsOfService();
|
||||
question = "Terms of service:\n\t" + tos.text.text;
|
||||
if (tos.minUserAge > 0) {
|
||||
question += "\n\tMinimum user age: " + tos.minUserAge;
|
||||
}
|
||||
if (tos.showPopup) {
|
||||
question += "\nPlease press enter.";
|
||||
if (customParameter instanceof StandardInputParameter inputParameter) {
|
||||
var parameter = inputParameter.value();
|
||||
switch (parameter) {
|
||||
case ASK_FIRST_NAME:
|
||||
question = "Enter first name";
|
||||
trim = true;
|
||||
} else {
|
||||
return Map.entry(Optional.of(question), Optional.of(""));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
question = parameter.toString();
|
||||
break;
|
||||
break;
|
||||
case ASK_LAST_NAME:
|
||||
question = "Enter last name";
|
||||
trim = true;
|
||||
break;
|
||||
case ASK_CODE:
|
||||
question = "Enter authentication code";
|
||||
ParameterInfoCode codeInfo = ((ParameterInfoCode) parameterInfo);
|
||||
question += "\n\tPhone number: " + codeInfo.getPhoneNumber();
|
||||
question += "\n\tTimeout: " + codeInfo.getTimeout() + " seconds";
|
||||
question += "\n\tCode type: " + codeInfo.getType().getClass().getSimpleName()
|
||||
.replace("AuthenticationCodeType", "");
|
||||
if (codeInfo.getNextType() != null) {
|
||||
question += "\n\tNext code type: " + codeInfo
|
||||
.getNextType()
|
||||
.getClass()
|
||||
.getSimpleName()
|
||||
.replace("AuthenticationCodeType", "");
|
||||
}
|
||||
trim = true;
|
||||
break;
|
||||
case ASK_PASSWORD:
|
||||
question = "Enter your password";
|
||||
String passwordMessage = "Password authorization:";
|
||||
String hint = ((ParameterInfoPasswordHint) parameterInfo).getHint();
|
||||
if (hint != null && !hint.isEmpty()) {
|
||||
passwordMessage += "\n\tHint: " + hint;
|
||||
}
|
||||
boolean hasRecoveryEmailAddress = ((ParameterInfoPasswordHint) parameterInfo)
|
||||
.hasRecoveryEmailAddress();
|
||||
passwordMessage += "\n\tHas recovery email: " + hasRecoveryEmailAddress;
|
||||
String recoveryEmailAddressPattern = ((ParameterInfoPasswordHint) parameterInfo)
|
||||
.getRecoveryEmailAddressPattern();
|
||||
if (recoveryEmailAddressPattern != null && !recoveryEmailAddressPattern.isEmpty()) {
|
||||
passwordMessage += "\n\tRecovery email address pattern: " + recoveryEmailAddressPattern;
|
||||
}
|
||||
System.out.println(passwordMessage);
|
||||
break;
|
||||
case NOTIFY_LINK:
|
||||
String link = ((ParameterInfoNotifyLink) parameterInfo).getLink();
|
||||
var sb = new StringBuilder();
|
||||
sb.append("Please confirm this login link on another device: " + link + "\n");
|
||||
sb.append("\n");
|
||||
sb.append(getQr(link) + "\n");
|
||||
sb.append("\n");
|
||||
return Map.entry(Optional.of("[" + who + "] " + sb), Optional.of(""));
|
||||
case TERMS_OF_SERVICE:
|
||||
TermsOfService tos = ((ParameterInfoTermsOfService) parameterInfo).getTermsOfService();
|
||||
question = "Terms of service:\n\t" + tos.text.text;
|
||||
if (tos.minUserAge > 0) {
|
||||
question += "\n\tMinimum user age: " + tos.minUserAge;
|
||||
}
|
||||
if (tos.showPopup) {
|
||||
question += "\nPlease press enter.";
|
||||
trim = true;
|
||||
} else {
|
||||
return Map.entry(Optional.of("[" + who + "] " + question), Optional.of(""));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
question = parameter.toString();
|
||||
break;
|
||||
}
|
||||
} else if (customParameter instanceof CliInputParameter inputParameter) {
|
||||
question = switch (inputParameter.value()) {
|
||||
case ASK_AUTH_DATA_TYPE -> "Please type the preferred auth mode [qr | token <token> | phone <phone number>]";
|
||||
default -> inputParameter.value().toString();
|
||||
};
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid custom parameter type: " + customParameter.toString());
|
||||
}
|
||||
return Map.entry(Optional.of(question), Optional.empty());
|
||||
return Map.entry(Optional.of("[" + who + "] " + question), Optional.empty());
|
||||
}
|
||||
|
||||
private static String getQr(String url) {
|
||||
|
|
127
src/main/java/picocli/shell/jline3/example/InstanceSettings.java
Normal file
127
src/main/java/picocli/shell/jline3/example/InstanceSettings.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
package picocli.shell.jline3.example;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import it.tdlight.client.APIToken;
|
||||
import it.tdlight.client.AuthenticationData;
|
||||
import it.tdlight.client.TDLibSettings;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class InstanceSettings {
|
||||
|
||||
public boolean useTestDatacenter;
|
||||
public String token;
|
||||
public Long phoneNumber;
|
||||
public Path path;
|
||||
public boolean fileDatabaseEnabled;
|
||||
public boolean chatInfoDatabaseEnabled;
|
||||
public boolean messageDatabaseEnabled;
|
||||
public String systemLanguageCode;
|
||||
public String deviceModel;
|
||||
public String systemVersion;
|
||||
public String applicationVersion;
|
||||
public boolean enableStorageOptimizer;
|
||||
public boolean ignoreFileNames;
|
||||
public String name;
|
||||
|
||||
public InstanceSettings(String name, Path path, TDLibSettings settings, AuthenticationData authenticationData) {
|
||||
this.name = name;
|
||||
this.useTestDatacenter = settings.isUsingTestDatacenter();
|
||||
if (authenticationData.isBot()) {
|
||||
this.token = authenticationData.getBotToken();
|
||||
} else if (!authenticationData.isQrCode()) {
|
||||
this.phoneNumber = authenticationData.getUserPhoneNumber();
|
||||
}
|
||||
this.path = path;
|
||||
this.fileDatabaseEnabled = settings.isFileDatabaseEnabled();
|
||||
this.chatInfoDatabaseEnabled = settings.isChatInfoDatabaseEnabled();
|
||||
this.messageDatabaseEnabled = settings.isMessageDatabaseEnabled();
|
||||
this.systemLanguageCode = settings.getSystemLanguageCode();
|
||||
this.deviceModel = settings.getDeviceModel();
|
||||
this.systemVersion = settings.getSystemVersion();
|
||||
this.applicationVersion = settings.getApplicationVersion();
|
||||
this.enableStorageOptimizer = settings.isStorageOptimizerEnabled();
|
||||
this.ignoreFileNames = settings.isIgnoreFileNames();
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public InstanceSettings(@JsonProperty("useTestDatacenter") boolean useTestDatacenter,
|
||||
@JsonProperty("token") String token,
|
||||
@JsonProperty("phoneNumber") Long phoneNumber,
|
||||
@JsonProperty("path") Path path,
|
||||
@JsonProperty("fileDatabaseEnabled") boolean fileDatabaseEnabled,
|
||||
@JsonProperty("chatInfoDatabaseEnabled") boolean chatInfoDatabaseEnabled,
|
||||
@JsonProperty("messageDatabaseEnabled") boolean messageDatabaseEnabled,
|
||||
@JsonProperty("systemLanguageCode") String systemLanguageCode,
|
||||
@JsonProperty("deviceModel") String deviceModel,
|
||||
@JsonProperty("systemVersion") String systemVersion,
|
||||
@JsonProperty("applicationVersion") String applicationVersion,
|
||||
@JsonProperty("enableStorageOptimizer") boolean enableStorageOptimizer,
|
||||
@JsonProperty("ignoreFileNames") boolean ignoreFileNames,
|
||||
@JsonProperty("name") String name) {
|
||||
this.name = name;
|
||||
this.useTestDatacenter = useTestDatacenter;
|
||||
this.token = token;
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.path = path;
|
||||
this.fileDatabaseEnabled = fileDatabaseEnabled;
|
||||
this.chatInfoDatabaseEnabled = chatInfoDatabaseEnabled;
|
||||
this.messageDatabaseEnabled = messageDatabaseEnabled;
|
||||
this.systemLanguageCode = systemLanguageCode;
|
||||
this.deviceModel = deviceModel;
|
||||
this.systemVersion = systemVersion;
|
||||
this.applicationVersion = applicationVersion;
|
||||
this.enableStorageOptimizer = enableStorageOptimizer;
|
||||
this.ignoreFileNames = ignoreFileNames;
|
||||
}
|
||||
|
||||
public static Path getDatabaseDirectoryPath(Path basePath) {
|
||||
return basePath.resolve("data");
|
||||
}
|
||||
|
||||
public static Path getDownloadsDirectoryPath(Path basePath) {
|
||||
return basePath.resolve("downloads");
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Path getDatabaseDirectoryPath() {
|
||||
return getDatabaseDirectoryPath(path);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Path getDownloadsDirectoryPath() {
|
||||
return getDownloadsDirectoryPath(path);
|
||||
}
|
||||
|
||||
public static void setTdPaths(TDLibSettings settings, Path path) {
|
||||
settings.setDatabaseDirectoryPath(getDatabaseDirectoryPath(path));
|
||||
settings.setDownloadedFilesDirectoryPath(getDownloadsDirectoryPath(path));
|
||||
}
|
||||
|
||||
public TDLibSettings toTDLibSettings(APIToken apiToken) {
|
||||
var settings = TDLibSettings.create(apiToken);
|
||||
settings.setUseTestDatacenter(useTestDatacenter);
|
||||
setTdPaths(settings, path);
|
||||
settings.setFileDatabaseEnabled(fileDatabaseEnabled);
|
||||
settings.setChatInfoDatabaseEnabled(chatInfoDatabaseEnabled);
|
||||
settings.setMessageDatabaseEnabled(messageDatabaseEnabled);
|
||||
settings.setSystemLanguageCode(systemLanguageCode);
|
||||
settings.setDeviceModel(deviceModel);
|
||||
settings.setSystemVersion(systemVersion);
|
||||
settings.setApplicationVersion(applicationVersion);
|
||||
settings.setEnableStorageOptimizer(enableStorageOptimizer);
|
||||
settings.setIgnoreFileNames(ignoreFileNames);
|
||||
return settings;
|
||||
}
|
||||
|
||||
public AuthenticationData toAuthData() {
|
||||
if (token != null) {
|
||||
return AuthenticationData.bot(token);
|
||||
} else if (phoneNumber != null) {
|
||||
return AuthenticationData.user(phoneNumber);
|
||||
} else {
|
||||
return AuthenticationData.qrCode();
|
||||
}
|
||||
}
|
||||
}
|
13
src/main/java/picocli/shell/jline3/example/TDException.java
Normal file
13
src/main/java/picocli/shell/jline3/example/TDException.java
Normal file
|
@ -0,0 +1,13 @@
|
|||
package picocli.shell.jline3.example;
|
||||
|
||||
import it.tdlight.jni.TdApi.Error;
|
||||
|
||||
public class TDException extends Exception {
|
||||
|
||||
public final int code;
|
||||
|
||||
public TDException(Error error) {
|
||||
super(error.code + ": " + error.message);
|
||||
this.code = error.code;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ package picocli.shell.jline3.example;
|
|||
|
||||
import it.tdlight.client.APIToken;
|
||||
import it.tdlight.client.AuthenticationData;
|
||||
import it.tdlight.client.InputParameter;
|
||||
import it.tdlight.client.ParameterInfo;
|
||||
import it.tdlight.client.SimpleTelegramClient;
|
||||
import it.tdlight.client.TDLibSettings;
|
||||
import it.tdlight.common.Init;
|
||||
|
@ -11,30 +13,58 @@ import it.tdlight.jni.TdApi.AuthorizationStateClosed;
|
|||
import it.tdlight.jni.TdApi.AuthorizationStateClosing;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateLoggingOut;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
import it.tdlight.jni.TdApi.MessageContent;
|
||||
import it.tdlight.jni.TdApi.MessageSender;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collection;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import picocli.shell.jline3.example.ClientInteractionRequest.CustomInputParameter.StandardInputParameter;
|
||||
|
||||
public class TDLibInstance {
|
||||
|
||||
private final String name;
|
||||
private Path path;
|
||||
private final TDLibSettings settings;
|
||||
private final Queue<ClientInteractionRequest> interactionRequests;
|
||||
private SimpleTelegramClient client;
|
||||
private AuthenticationData authenticationData;
|
||||
private final Long2ObjectLinkedOpenHashMap<ChatData> chats = new Long2ObjectLinkedOpenHashMap<>();
|
||||
|
||||
public TDLibInstance(String name, Queue<ClientInteractionRequest> interactionRequests) throws CantLoadLibrary {
|
||||
public TDLibInstance(APIToken apiToken, String name, Queue<ClientInteractionRequest> interactionRequests)
|
||||
throws CantLoadLibrary {
|
||||
Init.start();
|
||||
|
||||
var apiToken = APIToken.example();
|
||||
|
||||
this.name = name;
|
||||
settings = TDLibSettings.create(apiToken);
|
||||
setPath(name);
|
||||
this.interactionRequests = interactionRequests;
|
||||
}
|
||||
|
||||
public TDLibInstance(APIToken apiToken, InstanceSettings settings,
|
||||
Queue<ClientInteractionRequest> interactionRequests) throws CantLoadLibrary {
|
||||
Init.start();
|
||||
|
||||
this.name = settings.name;
|
||||
this.settings = settings.toTDLibSettings(apiToken);
|
||||
this.interactionRequests = interactionRequests;
|
||||
this.path = settings.path;
|
||||
}
|
||||
|
||||
public void setPath(String name) {
|
||||
var path = Path.of(".").resolve(name);
|
||||
settings.setDatabaseDirectoryPath(path.resolve("data"));
|
||||
settings.setDownloadedFilesDirectoryPath(path.resolve("downloads"));
|
||||
this.setPath(Path.of(".").resolve(name));
|
||||
}
|
||||
|
||||
public void setPath(Path path) {
|
||||
this.path = path;
|
||||
InstanceSettings.setTdPaths(settings, path);
|
||||
}
|
||||
|
||||
public TDLibSettings getSettings() {
|
||||
|
@ -50,11 +80,11 @@ public class TDLibInstance {
|
|||
throw new UnsupportedOperationException("Client is already started");
|
||||
}
|
||||
client = new SimpleTelegramClient(settings);
|
||||
client.setClientInteraction((parameter, parameterInfo, result) ->
|
||||
interactionRequests.offer(new ClientInteractionRequest(TDLibInstance.this, parameter, parameterInfo, result)));
|
||||
client.setClientInteraction(this::onClientInteraction);
|
||||
client.addUpdateHandler(TdApi.UpdateAuthorizationState.class, updateAuthorizationState -> {
|
||||
var state = updateAuthorizationState.authorizationState.getConstructor();
|
||||
if (state == AuthorizationStateReady.CONSTRUCTOR) {
|
||||
saveSettings();
|
||||
System.out.println("Logged in");
|
||||
}
|
||||
switch (state) {
|
||||
|
@ -64,9 +94,33 @@ public class TDLibInstance {
|
|||
AuthorizationStateClosing.CONSTRUCTOR -> Example.waitingLogin = false;
|
||||
}
|
||||
});
|
||||
client.addUpdateHandler(TdApi.UpdateNewChat.class, updateNewChat -> {
|
||||
var chat = chats.getAndMoveToFirst(updateNewChat.chat.id);
|
||||
if (chat == null) {
|
||||
chat = new ChatData();
|
||||
chats.putAndMoveToFirst(updateNewChat.chat.id, chat);
|
||||
}
|
||||
});
|
||||
client.start(authenticationData);
|
||||
}
|
||||
|
||||
private void onClientInteraction(InputParameter inputParameter,
|
||||
ParameterInfo parameterInfo,
|
||||
Consumer<String> result) {
|
||||
var customParameter = new StandardInputParameter(inputParameter);
|
||||
interactionRequests.offer(new ClientInteractionRequest(this, customParameter, parameterInfo, result));
|
||||
}
|
||||
|
||||
private void saveSettings() {
|
||||
var is = new InstanceSettings(name, path, settings, authenticationData);
|
||||
try (var os = Files.newOutputStream(path.resolve("settings.json"),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||
Example.mapper.writeValue(os, is);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationData getAuthenticationData() {
|
||||
return authenticationData;
|
||||
}
|
||||
|
@ -74,4 +128,43 @@ public class TDLibInstance {
|
|||
public void stop() throws InterruptedException {
|
||||
client.closeAndWait();
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public <T extends Function<R>, R extends TdApi.Object> CompletableFuture<R> send(T f) {
|
||||
var cf = new CompletableFuture<R>();
|
||||
client.send(f, result -> {
|
||||
if (result.isError()) {
|
||||
//noinspection OptionalGetWithoutIsPresent
|
||||
cf.completeExceptionally(new TDException(result.error().get()));
|
||||
} else {
|
||||
cf.complete(result.get());
|
||||
}
|
||||
});
|
||||
return cf;
|
||||
}
|
||||
|
||||
public Collection<Long> getChats() {
|
||||
return chats.keySet();
|
||||
}
|
||||
|
||||
public CompletableFuture<Chat> getSender(MessageSender senderId) {
|
||||
if (senderId instanceof TdApi.MessageSenderChat senderChat) {
|
||||
return send(new TdApi.GetChat(senderChat.chatId));
|
||||
} else if (senderId instanceof TdApi.MessageSenderUser senderUser) {
|
||||
return send(new TdApi.GetChat(senderUser.userId));
|
||||
} else {
|
||||
return CompletableFuture.failedFuture(new UnsupportedOperationException("Sender not supported: " + senderId));
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<String> contentToString(MessageContent content) {
|
||||
if (content instanceof TdApi.MessageText messageText) {
|
||||
return CompletableFuture.completedFuture(messageText.text.text);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture("(" + content.getClass().getSimpleName().substring("Message".length()) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
21
src/main/resources/log4j2.xml
Normal file
21
src/main/resources/log4j2.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration strict="true"
|
||||
xmlns="http://logging.apache.org/log4j/2.0/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://logging.apache.org/log4j/2.0/config
|
||||
https://raw.githubusercontent.com/apache/logging-log4j2/log4j-2.16.0/log4j-core/src/main/resources/Log4j-config.xsd"
|
||||
status="WARN">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout
|
||||
pattern="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} %style{%processId}{magenta} %style{%-20.20c{1}}{cyan} : %m%n%ex"/>
|
||||
</Console>
|
||||
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="it.tdlight.TDLight" level="ERROR" additivity="false" />
|
||||
<Root level="WARN">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
Loading…
Reference in New Issue
Block a user