diff options
| author | aunsane <aunsane@gmail.com> | 2018-04-27 21:33:17 +0300 | 
|---|---|---|
| committer | aunsane <aunsane@gmail.com> | 2018-04-27 21:33:17 +0300 | 
| commit | e1ec72eab6d00b3ba38e5932bc88920f103b6e4a (patch) | |
| tree | 999de2725a83e30fbbf6576200525d4ef0c5fe38 /protocols/Telegram/tdlib/td/example/java/org | |
| parent | b9ce1d4d98525490ca1a38e2d9fd4f3369adb3e0 (diff) | |
Telegram: initial commit
- tdlib moved to telegram dir
Diffstat (limited to 'protocols/Telegram/tdlib/td/example/java/org')
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  | 
