Write a Telegram client - easy


    How does Telegram differ from other popular messengers? He is open!
    Other messengers also have an API, but for some reason is the telegram known as the most open of the most popular ones?


    To begin with, Telegram has a really completely open client
    code . Unfortunately, we don’t see commits every day right on GitHub, but we have code under an open source license. Telegram architecture implies that both Bot and API have almost the same methods - https://core.telegram.org/methods .


    In fact, Telegram is not just a chat messenger, but a social platform, access to which is open to all sorts of applications. They can provide additional chips to users, instead of using a ready-made network of users and servers for delivering messages. It sounds so attractive that we wanted to try to write our "client" for Telegrams.


    The essence of the application


    We mainly deal with maps and navigation, so we immediately looked at something related to geolocation. I really liked that Telegram, before all other applications, had a convenient way to share a location in real time ( https://telegram.org/blog/live-locations ), and I use it quite often: to help a friend orient, show the way and most importantly answer the main question "When will you be?". In principle, this is enough for most people, but as always there are scenarios when there are not enough simple opportunities. For example, it may be a group of more than 10 people, with different devices (some devices may not be telephones) and different people. These people would be convenient to exchange messages in a group, as well as see each other moving on the map.


    At the forefront, we set the task of creating additional value for the Telegram, and not trying to use it for other purposes. We did not want people who do not have a special Telegram client, to see a mess of messages in a chat or something unintelligible. For people with an “improved” client, additional opportunities appear, for example:


    1. More refined time management when sending a location in real time to chat.
    2. View the location of contacts on the map.
    3. Connecting to the chat beacon devices via an external API (Bot).

    How we did it


    Fortunately, all the code we write is Open-Source, so I can immediately give a link to its implementation - Bot implementation and Komlin Telegram Client implementation .


    Bot - the basics

    There are a lot of documentation and examples on the implementation of the Bot, but I still want to go and tell about some of the pitfalls. To begin with, we wrote the server part
    in Java and chose the org.telegram: telegrambots library. Since our server is a regular SpringBoot, initialization is extremely simple:


    // Gradle implementation "org.telegram:telegrambots:3.6"
       TelegramBotsApi telegramBotsApi = new TelegramBotsApi();
       telegramBotsApi.registerBot(new TelegramLongPollingBot() {...});

    The main feature of the transfer location is that it needs to be updated frequently, and the bot needs to edit the already sent messages. If there were no such possibility, Bot would just start spamming the chat and it would, of course, be Epic Fail. Thank God, Telegram grants the bot the right to edit messages for 24 hours (at least, possibly longer).


    You can send a message in many ways. There is a type of Plain Text, Venue, Location, Game, Contact, Invoice, etc. It seemed that the Location was perfect for our task, but an unpleasant feature was revealed. Location can be transferred only from one device for one account or bot at a time! Imagine you have 2 phones and from two phones you sent your Location in one chat. So, an error will happen on the server and the first Location Sharing will just stop. It would seem that this is clearly a bad case, but imagine that you have a lot of Chinese beacons that can send Location to a given URL, but they don’t know how to send directly to Telegram. You write a Bot, which picks up from the server and pulls in telegrams. This is where it climbs out that the Bot will not be able to send more than one message to the location type. Turns out


    The solution is simple - send text messages, and the client will parse the text and show locations on the map. Unfortunately, only text messages will be visible in the standard Telegram client, but there you can insert a link to open the map.


    Bot - Reefs

    Unfortunately, Bot had to be rewritten 2.5 times. The main problem is the wrong design of communication.


    1. For some reason, at first it seemed like a good idea if the bot would be a full member of the chat and send messages. But, this is bad both in terms of privacy correspondence and in terms of interaction with the bot. The correct solution is to use inline bots . Thus, it is guaranteed that the bot does not see anything except its Location and can be used in any chat. Humanly speaking, it is uncivilized to drag your bot to some kind of general chat, but you need to talk with the bot one-on-one and set it up, and then he will be able to send the necessary messages to any selected chat.
    2. The Telegram Message API has 2 types of interaction historically: the buttons below the text ((inline buttons) [ https://core.telegram.org/bots/2-0-intro#switch-to-inline-buttons ]) and the answers to the bot directly text. In general, the answers with the bot are hopelessly outdated. Buttons are a bit more complicated in terms of implementation, but this completely pays off with ease of use and it is them that should be used for all non-text input.
    3. As an example of a bot, you can see the popular @vote_bot or our @osmand_bot.

    Telegram Client

    We did not manage to find examples of ready telegram clients other than the main one, but the rather simple tdlib structure helped us create the base client in just a couple of days.


    Gradle setting:
    task downloadTdLibzip {
        doLast {
            ant.get(src: 'https://core.telegram.org/tdlib/tdlib.zip', dest: 'tdlib.zip', skipexisting: 'true')
            ant.unzip(src: 'tdlib.zip', dest: 'tdlib/')
        }
    }
    task copyNativeLibs(type: Copy) {
        dependsOn downloadTdLibzip
        from "tdlib/libtd/src/main/libs"
        into "libs"
    }
    task copyJavaSources(type: Copy) {
        dependsOn downloadTdLibzip
        from "tdlib/libtd/src/main/java/org/drinkless/td"
        into "src/org/drinkless/td"
    }
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    }

    Almost all the insides of the Telegram are written in C ++ and from the Android point of view, only the 1.5 Mb API class of the proxy TdApi.java is visible . By comparing the documentation of the bots and the names of the methods, you can simply navigate where to go.


    Initializing a client with a global handler:
    funinit(): Boolean {
        returnif (libraryLoaded) {
            // create client
            client = Client.create(UpdatesHandler(), null, null)
            true
        } else {
            false
        }
    }

    User photo request:
    privatefunrequestUserPhoto(user: TdApi.User) {
        val remotePhoto = user.profilePhoto?.small?.remote
        if (remotePhoto != null && remotePhoto.id.isNotEmpty()) {
            downloadUserFilesMap[remotePhoto.id] = user
            client!!.send(TdApi.GetRemoteFile(remotePhoto.id, null)) { obj ->
                when (obj.constructor) {
                    TdApi.Error.CONSTRUCTOR -> {
                        val error = obj as TdApi.Error
                        val code = error.code
                        if (code != IGNORED_ERROR_CODE) {
                            listener?.onTelegramError(code, error.message)
                        }
                    }
                    TdApi.File.CONSTRUCTOR -> {
                        val file = obj as TdApi.File
                        client!!.send(TdApi.DownloadFile(file.id, 10), defaultHandler)
                    }
                    else -> listener?.onTelegramError(-1, "Receive wrong response from TDLib: $obj")
                }
            }
        }
    }

    Telegram Client - pitfalls

    1. Registration / Login and Logout. When registering, it is necessary to take into account different scenarios: when an access code is sent by SMS or to another telegram client, two-factor authorization, etc. The biggest challenge is testing. Any authorization more than 3 times led to account blocking for 24 hours, so testing Logout was especially fun. Despite the fact that registration is needed only once, this is probably the most difficult part of the integration.
    2. Determine how and in what order to read messages. Any client has access to all messages in all chats, but they must be read sequentially. In our case, 99% of the messages need to be discarded. At first, for some reason, we made reading all the messages for the last 3 days at login, but later it only caused problems and when restarting, we lost the messages. Therefore, now we read only new messages, and for those messages that we need, we store id in the internal database.

    What happened


    Probably, knowing all the pitfalls could all be done at times faster, but it turned out somewhere 1-2 months for three people. The final application can be found on Google Play .



    The main question in this story is how correct this interaction is from the point of view of the Telegram and whether the users will like this kind of integration. In any case, the idea itself is niche and it has already found individual customers.


    I will be glad to answer your questions.


    Also popular now: