summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/example/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/example/java/org')
-rw-r--r--protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Client.java285
-rw-r--r--protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Log.java75
-rw-r--r--protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/example/Example.java533
3 files changed, 893 insertions, 0 deletions
diff --git a/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Client.java b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Client.java
new file mode 100644
index 0000000000..efb38e9c5a
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Client.java
@@ -0,0 +1,285 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+package org.drinkless.tdlib;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Main class for interaction with the TDLib.
+ */
+public final class Client implements Runnable {
+ /**
+ * Interface for handler for results of queries to TDLib and incoming updates from TDLib.
+ */
+ public interface ResultHandler {
+ /**
+ * Callback called on result of query to TDLib or incoming update from TDLib.
+ *
+ * @param object Result of query or update of type TdApi.Update about new events.
+ */
+ void onResult(TdApi.Object object);
+ }
+
+ /**
+ * Interface for handler of exceptions thrown while invoking ResultHandler.
+ * By default, all such exceptions are ignored.
+ * All exceptions thrown from ExceptionHandler are ignored.
+ */
+ public interface ExceptionHandler {
+ /**
+ * Callback called on exceptions thrown while invoking ResultHandler.
+ *
+ * @param e Exception thrown by ResultHandler.
+ */
+ void onException(Throwable e);
+ }
+
+ /**
+ * Sends a request to the TDLib.
+ *
+ * @param query Object representing a query to the TDLib.
+ * @param resultHandler Result handler with onResult method which will be called with result
+ * of the query or with TdApi.Error as parameter. If it is null, nothing
+ * will be called.
+ * @param exceptionHandler Exception handler with onException method which will be called on
+ * exception thrown from resultHandler. If it is null, then
+ * defaultExceptionHandler will be called.
+ * @throws NullPointerException if query is null.
+ */
+ public void send(TdApi.Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
+ if (query == null) {
+ throw new NullPointerException("query is null");
+ }
+
+ readLock.lock();
+ try {
+ if (isClientDestroyed) {
+ if (resultHandler != null) {
+ handleResult(new TdApi.Error(500, "Client is closed"), resultHandler, exceptionHandler);
+ }
+ return;
+ }
+
+ long queryId = currentQueryId.incrementAndGet();
+ handlers.put(queryId, new Handler(resultHandler, exceptionHandler));
+ nativeClientSend(nativeClientId, queryId, query);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Sends a request to the TDLib with an empty ExceptionHandler.
+ *
+ * @param query Object representing a query to the TDLib.
+ * @param resultHandler Result handler with onResult method which will be called with result
+ * of the query or with TdApi.Error as parameter. If it is null, then
+ * defaultExceptionHandler will be called.
+ * @throws NullPointerException if query is null.
+ */
+ public void send(TdApi.Function query, ResultHandler resultHandler) {
+ send(query, resultHandler, null);
+ }
+
+ /**
+ * Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously.
+ *
+ * @param query Object representing a query to the TDLib.
+ * @return request result.
+ * @throws NullPointerException if query is null.
+ */
+ public static TdApi.Object execute(TdApi.Function query) {
+ if (query == null) {
+ throw new NullPointerException("query is null");
+ }
+ return nativeClientExecute(query);
+ }
+
+ /**
+ * Replaces handler for incoming updates from the TDLib.
+ *
+ * @param updatesHandler Handler with onResult method which will be called for every incoming
+ * update from the TDLib.
+ * @param exceptionHandler Exception handler with onException method which will be called on
+ * exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked.
+ */
+ public void setUpdatesHandler(ResultHandler updatesHandler, ExceptionHandler exceptionHandler) {
+ handlers.put(0L, new Handler(updatesHandler, exceptionHandler));
+ }
+
+ /**
+ * Replaces handler for incoming updates from the TDLib. Sets empty ExceptionHandler.
+ *
+ * @param updatesHandler Handler with onResult method which will be called for every incoming
+ * update from the TDLib.
+ */
+ public void setUpdatesHandler(ResultHandler updatesHandler) {
+ setUpdatesHandler(updatesHandler, null);
+ }
+
+ /**
+ * Replaces default exception handler to be invoked on exceptions thrown from updatesHandler and all other ResultHandler.
+ *
+ * @param defaultExceptionHandler Default exception handler. If null Exceptions are ignored.
+ */
+ public void setDefaultExceptionHandler(Client.ExceptionHandler defaultExceptionHandler) {
+ this.defaultExceptionHandler = defaultExceptionHandler;
+ }
+
+ /**
+ * Overridden method from Runnable, do not call it directly.
+ */
+ @Override
+ public void run() {
+ while (!stopFlag) {
+ receiveQueries(300.0 /*seconds*/);
+ }
+ }
+
+ /**
+ * Creates new Client.
+ *
+ * @param updatesHandler Handler for incoming updates.
+ * @param updatesExceptionHandler Handler for exceptions thrown from updatesHandler. If it is null, exceptions will be iggnored.
+ * @param defaultExceptionHandler Default handler for exceptions thrown from all ResultHandler. If it is null, exceptions will be iggnored.
+ * @return created Client
+ */
+ public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) {
+ Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler);
+ new Thread(client, "TDLib thread").start();
+ return client;
+ }
+
+ /**
+ * Closes Client.
+ */
+ public void close() {
+ writeLock.lock();
+ try {
+ if (isClientDestroyed) {
+ return;
+ }
+ if (!stopFlag) {
+ send(new TdApi.Close(), null);
+ }
+ isClientDestroyed = true;
+ while (!stopFlag) {
+ Thread.yield();
+ }
+ while (handlers.size() != 1) {
+ receiveQueries(300.0);
+ }
+ destroyNativeClient(nativeClientId);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
+ private final Lock readLock = readWriteLock.readLock();
+ private final Lock writeLock = readWriteLock.writeLock();
+
+ private volatile boolean stopFlag = false;
+ private volatile boolean isClientDestroyed = false;
+ private final long nativeClientId;
+
+ private final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<Long, Handler>();
+ private final AtomicLong currentQueryId = new AtomicLong();
+
+ private volatile ExceptionHandler defaultExceptionHandler = null;
+
+ private static final int MAX_EVENTS = 1000;
+ private final long[] eventIds = new long[MAX_EVENTS];
+ private final TdApi.Object[] events = new TdApi.Object[MAX_EVENTS];
+
+ private static class Handler {
+ final ResultHandler resultHandler;
+ final ExceptionHandler exceptionHandler;
+
+ Handler(ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
+ this.resultHandler = resultHandler;
+ this.exceptionHandler = exceptionHandler;
+ }
+ }
+
+ private Client(ResultHandler updatesHandler, ExceptionHandler updateExceptionHandler, ExceptionHandler defaultExceptionHandler) {
+ nativeClientId = createNativeClient();
+ handlers.put(0L, new Handler(updatesHandler, updateExceptionHandler));
+ this.defaultExceptionHandler = defaultExceptionHandler;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void processResult(long id, TdApi.Object object) {
+ if (object instanceof TdApi.UpdateAuthorizationState) {
+ if (((TdApi.UpdateAuthorizationState) object).authorizationState instanceof TdApi.AuthorizationStateClosed) {
+ stopFlag = true;
+ }
+ }
+ Handler handler;
+ if (id == 0) {
+ // update handler stays forever
+ handler = handlers.get(id);
+ } else {
+ handler = handlers.remove(id);
+ }
+ if (handler == null) {
+ return;
+ }
+
+ handleResult(object, handler.resultHandler, handler.exceptionHandler);
+ }
+
+ private void handleResult(TdApi.Object object, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
+ if (resultHandler == null) {
+ return;
+ }
+
+ try {
+ resultHandler.onResult(object);
+ } catch (Throwable cause) {
+ if (exceptionHandler == null) {
+ exceptionHandler = defaultExceptionHandler;
+ }
+ if (exceptionHandler != null) {
+ try {
+ exceptionHandler.onException(cause);
+ } catch (Throwable ignored) {
+ }
+ }
+ }
+ }
+
+ private void receiveQueries(double timeout) {
+ int resultN = nativeClientReceive(nativeClientId, eventIds, events, timeout);
+ for (int i = 0; i < resultN; i++) {
+ processResult(eventIds[i], events[i]);
+ events[i] = null;
+ }
+ }
+
+ private static native long createNativeClient();
+
+ private static native void nativeClientSend(long nativeClientId, long eventId, TdApi.Function function);
+
+ private static native int nativeClientReceive(long nativeClientId, long[] eventIds, TdApi.Object[] events, double timeout);
+
+ private static native TdApi.Object nativeClientExecute(TdApi.Function function);
+
+ private static native void destroyNativeClient(long nativeClientId);
+}
diff --git a/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Log.java b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Log.java
new file mode 100644
index 0000000000..c81ffbeeb7
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Log.java
@@ -0,0 +1,75 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+package org.drinkless.tdlib;
+
+/**
+ * Class for managing internal TDLib logging.
+ */
+public final class Log {
+ /**
+ * Changes TDLib log verbosity.
+ *
+ * @param verbosityLevel New value of log verbosity level. Must be non-negative.
+ * Value 0 corresponds to fatal errors,
+ * value 1 corresponds to java.util.logging.Level.SEVERE,
+ * value 2 corresponds to java.util.logging.Level.WARNING,
+ * value 3 corresponds to java.util.logging.Level.INFO,
+ * value 4 corresponds to java.util.logging.Level.FINE,
+ * value 5 corresponds to java.util.logging.Level.FINER,
+ * value greater than 5 can be used to enable even more logging.
+ * Default value of the log verbosity level is 5.
+ */
+ public static native void setVerbosityLevel(int verbosityLevel);
+
+ /**
+ * Sets file path for writing TDLib internal log. By default TDLib writes logs to the System.err.
+ * Use this method to write the log to a file instead.
+ *
+ * @param filePath Path to a file for writing TDLib internal log. Use an empty path to
+ * switch back to logging to the System.err.
+ * @return whether opening the log file succeeded.
+ */
+ public static native boolean setFilePath(String filePath);
+
+ /**
+ * Changes maximum size of TDLib log file.
+ *
+ * @param maxFileSize Maximum size of the file to where the internal TDLib log is written
+ * before the file will be auto-rotated. Must be positive. Defaults to 10 MB.
+ */
+ public static native void setMaxFileSize(long maxFileSize);
+
+ /**
+ * This function is called from the JNI when a fatal error happens to provide a better error message.
+ * The function does not return.
+ *
+ * @param errorMessage Error message.
+ */
+ private static void onFatalError(String errorMessage) {
+ class ThrowError implements Runnable {
+ private ThrowError(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ @Override
+ public void run() {
+ throw new RuntimeException("TDLib fatal error: " + errorMessage);
+ }
+
+ private final String errorMessage;
+ }
+
+ new Thread(new ThrowError(errorMessage), "TDLib fatal error thread").start();
+ while (true) {
+ try {
+ Thread.sleep(1000); // milliseconds
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/example/Example.java b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/example/Example.java
new file mode 100644
index 0000000000..831de88f1d
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/example/Example.java
@@ -0,0 +1,533 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+package org.drinkless.tdlib.example;
+
+import org.drinkless.tdlib.Client;
+import org.drinkless.tdlib.Log;
+import org.drinkless.tdlib.TdApi;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.NavigableSet;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Example class for TDLib usage from Java.
+ */
+public final class Example {
+ private static Client client = null;
+
+ private static TdApi.AuthorizationState authorizationState = null;
+ private static volatile boolean haveAuthorization = false;
+ private static volatile boolean quiting = false;
+
+ private static final Client.ResultHandler defaultHandler = new DefaultHandler();
+
+ private static final Lock authorizationLock = new ReentrantLock();
+ private static final Condition gotAuthorization = authorizationLock.newCondition();
+
+ private static final ConcurrentMap<Integer, TdApi.User> users = new ConcurrentHashMap<Integer, TdApi.User>();
+ private static final ConcurrentMap<Integer, TdApi.BasicGroup> basicGroups = new ConcurrentHashMap<Integer, TdApi.BasicGroup>();
+ private static final ConcurrentMap<Integer, TdApi.Supergroup> supergroups = new ConcurrentHashMap<Integer, TdApi.Supergroup>();
+ private static final ConcurrentMap<Integer, TdApi.SecretChat> secretChats = new ConcurrentHashMap<Integer, TdApi.SecretChat>();
+
+ private static final ConcurrentMap<Long, TdApi.Chat> chats = new ConcurrentHashMap<Long, TdApi.Chat>();
+ private static final NavigableSet<OrderedChat> chatList = new TreeSet<OrderedChat>();
+ private static boolean haveFullChatList = false;
+
+ private static final ConcurrentMap<Integer, TdApi.UserFullInfo> usersFullInfo = new ConcurrentHashMap<Integer, TdApi.UserFullInfo>();
+ private static final ConcurrentMap<Integer, TdApi.BasicGroupFullInfo> basicGroupsFullInfo = new ConcurrentHashMap<Integer, TdApi.BasicGroupFullInfo>();
+ private static final ConcurrentMap<Integer, TdApi.SupergroupFullInfo> supergroupsFullInfo = new ConcurrentHashMap<Integer, TdApi.SupergroupFullInfo>();
+
+ private static final String newLine = System.getProperty("line.separator");
+ private static final String commandsLine = "Enter command (gcs - GetChats, gc <chatId> - GetChat, me - GetMe, sm <chatId> <message> - SendMessage, lo - LogOut, q - Quit): ";
+ private static volatile String currentPrompt = null;
+
+ static {
+ System.loadLibrary("tdjni");
+ }
+
+ private static void print(String str) {
+ if (currentPrompt != null) {
+ System.out.println("");
+ }
+ System.out.println(str);
+ if (currentPrompt != null) {
+ System.out.print(currentPrompt);
+ }
+ }
+
+ private static void setChatOrder(TdApi.Chat chat, long order) {
+ synchronized (chatList) {
+ if (chat.order != 0) {
+ boolean isRemoved = chatList.remove(new OrderedChat(chat.order, chat.id));
+ assert isRemoved;
+ }
+
+ chat.order = order;
+
+ if (chat.order != 0) {
+ boolean isAdded = chatList.add(new OrderedChat(chat.order, chat.id));
+ assert isAdded;
+ }
+ }
+ }
+
+ private static void onAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState) {
+ if (authorizationState != null) {
+ Example.authorizationState = authorizationState;
+ }
+ switch (Example.authorizationState.getConstructor()) {
+ case TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR:
+ TdApi.TdlibParameters parameters = new TdApi.TdlibParameters();
+ parameters.databaseDirectory = "tdlib";
+ parameters.useMessageDatabase = true;
+ parameters.useSecretChats = true;
+ parameters.apiId = 94575;
+ parameters.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
+ parameters.systemLanguageCode = "en";
+ parameters.deviceModel = "Desktop";
+ parameters.systemVersion = "Unknown";
+ parameters.applicationVersion = "1.0";
+ parameters.enableStorageOptimizer = true;
+
+ client.send(new TdApi.SetTdlibParameters(parameters), new AuthorizationRequestHandler());
+ break;
+ case TdApi.AuthorizationStateWaitEncryptionKey.CONSTRUCTOR:
+ client.send(new TdApi.CheckDatabaseEncryptionKey(), new AuthorizationRequestHandler());
+ break;
+ case TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR: {
+ String phoneNumber = promptString("Please enter phone number: ");
+ client.send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, false, false), new AuthorizationRequestHandler());
+ break;
+ }
+ case TdApi.AuthorizationStateWaitCode.CONSTRUCTOR: {
+ String code = promptString("Please enter authentication code: ");
+ client.send(new TdApi.CheckAuthenticationCode(code, "", ""), new AuthorizationRequestHandler());
+ break;
+ }
+ case TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR: {
+ String password = promptString("Please enter password: ");
+ client.send(new TdApi.CheckAuthenticationPassword(password), new AuthorizationRequestHandler());
+ break;
+ }
+ case TdApi.AuthorizationStateReady.CONSTRUCTOR:
+ haveAuthorization = true;
+ authorizationLock.lock();
+ try {
+ gotAuthorization.signal();
+ } finally {
+ authorizationLock.unlock();
+ }
+ break;
+ case TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR:
+ haveAuthorization = false;
+ print("Logging out");
+ break;
+ case TdApi.AuthorizationStateClosing.CONSTRUCTOR:
+ haveAuthorization = false;
+ print("Closing");
+ break;
+ case TdApi.AuthorizationStateClosed.CONSTRUCTOR:
+ print("Closed");
+ if (!quiting) {
+ client = Client.create(new UpdatesHandler(), null, null); // recreate client after previous has closed
+ }
+ break;
+ default:
+ System.err.println("Unsupported authorization state:" + newLine + Example.authorizationState);
+ }
+ }
+
+ private static int toInt(String arg) {
+ int result = 0;
+ try {
+ result = Integer.parseInt(arg);
+ } catch (NumberFormatException ignored) {
+ }
+ return result;
+ }
+
+ private static long getChatId(String arg) {
+ long chatId = 0;
+ try {
+ chatId = Long.parseLong(arg);
+ } catch (NumberFormatException ignored) {
+ }
+ return chatId;
+ }
+
+ private static String promptString(String prompt) {
+ System.out.print(prompt);
+ currentPrompt = prompt;
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+ String str = "";
+ try {
+ str = reader.readLine();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ currentPrompt = null;
+ return str;
+ }
+
+ private static void getCommand() {
+ String command = promptString(commandsLine);
+ String[] commands = command.split(" ", 2);
+ try {
+ switch (commands[0]) {
+ case "gcs": {
+ int limit = 20;
+ if (commands.length > 1) {
+ limit = toInt(commands[1]);
+ }
+ getChatList(limit);
+ break;
+ }
+ case "gc":
+ client.send(new TdApi.GetChat(getChatId(commands[1])), defaultHandler);
+ break;
+ case "me":
+ client.send(new TdApi.GetMe(), defaultHandler);
+ break;
+ case "sm": {
+ String[] args = commands[1].split(" ", 2);
+ sendMessage(getChatId(args[0]), args[1]);
+ break;
+ }
+ case "lo":
+ haveAuthorization = false;
+ client.send(new TdApi.LogOut(), defaultHandler);
+ break;
+ case "q":
+ quiting = true;
+ haveAuthorization = false;
+ client.send(new TdApi.Close(), defaultHandler);
+ break;
+ default:
+ System.err.println("Unsupported command: " + command);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ print("Not enough arguments");
+ }
+ }
+
+ private static void getChatList(final int limit) {
+ synchronized (chatList) {
+ if (!haveFullChatList && limit > chatList.size()) {
+ // have enough chats in the chat list or chat list is too small
+ long offsetOrder = Long.MAX_VALUE;
+ long offsetChatId = 0;
+ if (!chatList.isEmpty()) {
+ OrderedChat last = chatList.last();
+ offsetOrder = last.order;
+ offsetChatId = last.chatId;
+ }
+ client.send(new TdApi.GetChats(offsetOrder, offsetChatId, limit - chatList.size()), new Client.ResultHandler() {
+ @Override
+ public void onResult(TdApi.Object object) {
+ switch (object.getConstructor()) {
+ case TdApi.Error.CONSTRUCTOR:
+ System.err.println("Receive an error for GetChats:" + newLine + object);
+ break;
+ case TdApi.Chats.CONSTRUCTOR:
+ long[] chatIds = ((TdApi.Chats) object).chatIds;
+ if (chatIds.length == 0) {
+ synchronized (chatList) {
+ haveFullChatList = true;
+ }
+ }
+ // chats had already been received through updates, let's retry request
+ getChatList(limit);
+ break;
+ default:
+ System.err.println("Receive wrong response from TDLib:" + newLine + object);
+ }
+ }
+ });
+ return;
+ }
+
+ // have enough chats in the chat list to answer request
+ java.util.Iterator<OrderedChat> iter = chatList.iterator();
+ System.out.println();
+ System.out.println("First " + limit + " chat(s) out of " + chatList.size() + " known chat(s):");
+ for (int i = 0; i < limit; i++) {
+ long chatId = iter.next().chatId;
+ TdApi.Chat chat = chats.get(chatId);
+ synchronized (chat) {
+ System.out.println(chatId + ": " + chat.title);
+ }
+ }
+ print("");
+ }
+ }
+
+ private static void sendMessage(long chatId, String message) {
+ // initialize reply markup just for testing
+ TdApi.InlineKeyboardButton[] row = {new TdApi.InlineKeyboardButton("https://telegram.org?1", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?2", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?3", new TdApi.InlineKeyboardButtonTypeUrl())};
+ TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][]{row, row, row});
+
+ TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), false, true);
+ client.send(new TdApi.SendMessage(chatId, 0, false, false, replyMarkup, content), defaultHandler);
+ }
+
+ public static void main(String[] args) throws InterruptedException {
+ // disable TDLib log
+ Log.setVerbosityLevel(0);
+ if (!Log.setFilePath("tdlib.log")) {
+ throw new IOError(new IOException("Write access to the current directory is required"));
+ }
+
+ // create client
+ client = Client.create(new UpdatesHandler(), null, null);
+
+ // test Client.execute
+ defaultHandler.onResult(Client.execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test")));
+
+ // main loop
+ while (!quiting) {
+ // await authorization
+ authorizationLock.lock();
+ try {
+ while (!haveAuthorization) {
+ gotAuthorization.await();
+ }
+ } finally {
+ authorizationLock.unlock();
+ }
+
+ while (haveAuthorization) {
+ getCommand();
+ }
+ }
+ }
+
+ private static class OrderedChat implements Comparable<OrderedChat> {
+ final long order;
+ final long chatId;
+
+ OrderedChat(long order, long chatId) {
+ this.order = order;
+ this.chatId = chatId;
+ }
+
+ @Override
+ public int compareTo(OrderedChat o) {
+ if (this.order != o.order) {
+ return o.order < this.order ? -1 : 1;
+ }
+ if (this.chatId != o.chatId) {
+ return o.chatId < this.chatId ? -1 : 1;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ OrderedChat o = (OrderedChat) obj;
+ return this.order == o.order && this.chatId == o.chatId;
+ }
+ }
+
+ private static class DefaultHandler implements Client.ResultHandler {
+ @Override
+ public void onResult(TdApi.Object object) {
+ print(object.toString());
+ }
+ }
+
+ private static class UpdatesHandler implements Client.ResultHandler {
+ @Override
+ public void onResult(TdApi.Object object) {
+ switch (object.getConstructor()) {
+ case TdApi.UpdateAuthorizationState.CONSTRUCTOR:
+ onAuthorizationStateUpdated(((TdApi.UpdateAuthorizationState) object).authorizationState);
+ break;
+
+ case TdApi.UpdateUser.CONSTRUCTOR:
+ TdApi.UpdateUser updateUser = (TdApi.UpdateUser) object;
+ users.put(updateUser.user.id, updateUser.user);
+ break;
+ case TdApi.UpdateUserStatus.CONSTRUCTOR: {
+ TdApi.UpdateUserStatus updateUserStatus = (TdApi.UpdateUserStatus) object;
+ TdApi.User user = users.get(updateUserStatus.userId);
+ synchronized (user) {
+ user.status = updateUserStatus.status;
+ }
+ break;
+ }
+ case TdApi.UpdateBasicGroup.CONSTRUCTOR:
+ TdApi.UpdateBasicGroup updateBasicGroup = (TdApi.UpdateBasicGroup) object;
+ basicGroups.put(updateBasicGroup.basicGroup.id, updateBasicGroup.basicGroup);
+ break;
+ case TdApi.UpdateSupergroup.CONSTRUCTOR:
+ TdApi.UpdateSupergroup updateSupergroup = (TdApi.UpdateSupergroup) object;
+ supergroups.put(updateSupergroup.supergroup.id, updateSupergroup.supergroup);
+ break;
+ case TdApi.UpdateSecretChat.CONSTRUCTOR:
+ TdApi.UpdateSecretChat updateSecretChat = (TdApi.UpdateSecretChat) object;
+ secretChats.put(updateSecretChat.secretChat.id, updateSecretChat.secretChat);
+ break;
+
+ case TdApi.UpdateNewChat.CONSTRUCTOR: {
+ TdApi.UpdateNewChat updateNewChat = (TdApi.UpdateNewChat) object;
+ TdApi.Chat chat = updateNewChat.chat;
+ synchronized (chat) {
+ chats.put(chat.id, chat);
+
+ long order = chat.order;
+ chat.order = 0;
+ setChatOrder(chat, order);
+ }
+ break;
+ }
+ case TdApi.UpdateChatTitle.CONSTRUCTOR: {
+ TdApi.UpdateChatTitle updateChat = (TdApi.UpdateChatTitle) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.title = updateChat.title;
+ }
+ break;
+ }
+ case TdApi.UpdateChatPhoto.CONSTRUCTOR: {
+ TdApi.UpdateChatPhoto updateChat = (TdApi.UpdateChatPhoto) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.photo = updateChat.photo;
+ }
+ break;
+ }
+ case TdApi.UpdateChatLastMessage.CONSTRUCTOR: {
+ TdApi.UpdateChatLastMessage updateChat = (TdApi.UpdateChatLastMessage) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.lastMessage = updateChat.lastMessage;
+ setChatOrder(chat, updateChat.order);
+ }
+ break;
+ }
+ case TdApi.UpdateChatOrder.CONSTRUCTOR: {
+ TdApi.UpdateChatOrder updateChat = (TdApi.UpdateChatOrder) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ setChatOrder(chat, updateChat.order);
+ }
+ break;
+ }
+ case TdApi.UpdateChatIsPinned.CONSTRUCTOR: {
+ TdApi.UpdateChatIsPinned updateChat = (TdApi.UpdateChatIsPinned) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.isPinned = updateChat.isPinned;
+ setChatOrder(chat, updateChat.order);
+ }
+ break;
+ }
+ case TdApi.UpdateChatReadInbox.CONSTRUCTOR: {
+ TdApi.UpdateChatReadInbox updateChat = (TdApi.UpdateChatReadInbox) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.lastReadInboxMessageId = updateChat.lastReadInboxMessageId;
+ chat.unreadCount = updateChat.unreadCount;
+ }
+ break;
+ }
+ case TdApi.UpdateChatReadOutbox.CONSTRUCTOR: {
+ TdApi.UpdateChatReadOutbox updateChat = (TdApi.UpdateChatReadOutbox) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.lastReadOutboxMessageId = updateChat.lastReadOutboxMessageId;
+ }
+ break;
+ }
+ case TdApi.UpdateChatUnreadMentionCount.CONSTRUCTOR: {
+ TdApi.UpdateChatUnreadMentionCount updateChat = (TdApi.UpdateChatUnreadMentionCount) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.unreadMentionCount = updateChat.unreadMentionCount;
+ }
+ break;
+ }
+ case TdApi.UpdateMessageMentionRead.CONSTRUCTOR: {
+ TdApi.UpdateMessageMentionRead updateChat = (TdApi.UpdateMessageMentionRead) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.unreadMentionCount = updateChat.unreadMentionCount;
+ }
+ break;
+ }
+ case TdApi.UpdateChatReplyMarkup.CONSTRUCTOR: {
+ TdApi.UpdateChatReplyMarkup updateChat = (TdApi.UpdateChatReplyMarkup) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.replyMarkupMessageId = updateChat.replyMarkupMessageId;
+ }
+ break;
+ }
+ case TdApi.UpdateChatDraftMessage.CONSTRUCTOR: {
+ TdApi.UpdateChatDraftMessage updateChat = (TdApi.UpdateChatDraftMessage) object;
+ TdApi.Chat chat = chats.get(updateChat.chatId);
+ synchronized (chat) {
+ chat.draftMessage = updateChat.draftMessage;
+ setChatOrder(chat, updateChat.order);
+ }
+ break;
+ }
+ case TdApi.UpdateNotificationSettings.CONSTRUCTOR: {
+ TdApi.UpdateNotificationSettings update = (TdApi.UpdateNotificationSettings) object;
+ if (update.scope instanceof TdApi.NotificationSettingsScopeChat) {
+ TdApi.Chat chat = chats.get(((TdApi.NotificationSettingsScopeChat) update.scope).chatId);
+ synchronized (chat) {
+ chat.notificationSettings = update.notificationSettings;
+ }
+ }
+ break;
+ }
+
+ case TdApi.UpdateUserFullInfo.CONSTRUCTOR:
+ TdApi.UpdateUserFullInfo updateUserFullInfo = (TdApi.UpdateUserFullInfo) object;
+ usersFullInfo.put(updateUserFullInfo.userId, updateUserFullInfo.userFullInfo);
+ break;
+ case TdApi.UpdateBasicGroupFullInfo.CONSTRUCTOR:
+ TdApi.UpdateBasicGroupFullInfo updateBasicGroupFullInfo = (TdApi.UpdateBasicGroupFullInfo) object;
+ basicGroupsFullInfo.put(updateBasicGroupFullInfo.basicGroupId, updateBasicGroupFullInfo.basicGroupFullInfo);
+ break;
+ case TdApi.UpdateSupergroupFullInfo.CONSTRUCTOR:
+ TdApi.UpdateSupergroupFullInfo updateSupergroupFullInfo = (TdApi.UpdateSupergroupFullInfo) object;
+ supergroupsFullInfo.put(updateSupergroupFullInfo.supergroupId, updateSupergroupFullInfo.supergroupFullInfo);
+ break;
+ default:
+ // print("Unsupported update:" + newLine + object);
+ }
+ }
+ }
+
+ private static class AuthorizationRequestHandler implements Client.ResultHandler {
+ @Override
+ public void onResult(TdApi.Object object) {
+ switch (object.getConstructor()) {
+ case TdApi.Error.CONSTRUCTOR:
+ System.err.println("Receive an error:" + newLine + object);
+ onAuthorizationStateUpdated(null); // repeat last action
+ break;
+ case TdApi.Ok.CONSTRUCTOR:
+ // result is already received through UpdateAuthorizationState, nothing to do
+ break;
+ default:
+ System.err.println("Receive wrong response from TDLib:" + newLine + object);
+ }
+ }
+ }
+} \ No newline at end of file