📜 ⬆️ ⬇️

Instructions: How to create bots in Telegram

June 24 Telegram developers have opened a platform for creating bots. The news has bypassed Habr, but many have already begun to develop quizzes. At the same time, at least some examples of working bots are indicated.

First of all, the Telegram bot is still an application running on your side and making requests to the Telegram Bot API . Moreover, the API is a simple content - the bot accesses a specific URL with parameters, and Telegram responds with a JSON object.

Consider the API on the example of creating a trivial bot:

1. Registration


Before starting development, a bot must register and receive its unique id, which is also a token. For this, there is a special bot in Telegram - @BotFather .
')
We write him / start and get a list of all his commands.
The first and main - / newbot - is sent to him and the bot asks to invent a name for our new bot. The only restriction on the name - in the end it must end in «bot». If successful, BotFather returns the bot token and a link to quickly add the bot to the contacts, otherwise you will have to break your head over the name.

To get started, this is enough. Particularly pedantic can assign a bot avatar, a description and a welcome message here.

Do not forget to check the received token using the api.telegram.org/bot<TOKEN>/getMe link, they say, it does not always work the first time.

2. Programming


I will create a bot on Python3, however, due to the adequacy of this language, algorithms are easily transferred to any other.

Telegram allows you not to upload messages manually, but to put webHook, and then they will send each message themselves. For Python, in order not to bother with cgi and threads, it is convenient to use some kind of reactor, so I chose tornado.web for implementation. (for GAE it is convenient to use a bunch of Python2 + Flask)

Bot frame:

URL = "https://api.telegram.org/bot%s/" % BOT_TOKEN MyURL = "https://example.com/hook" api = requests.Session() application = tornado.web.Application([ (r"/", Handler), ]) if __name__ == '__main__': signal.signal(signal.SIGTERM, signal_term_handler) try: set_hook = api.get(URL + "setWebhook?url=%s" % MyURL) if set_hook.status_code != 200: logging.error("Can't set hook: %s. Quit." % set_hook.text) exit(1) application.listen(8888) tornado.ioloop.IOLoop.current().start() except KeyboardInterrupt: signal_term_handler(signal.SIGTERM, None) 

Here, when we start the bot, we install a webhost to our address and catch the exit signal to return the behavior with manual event upload.

The tornado application for processing requests takes the class tornado.web.RequestHandler, in which will be the logic of the bot.

 class Handler(tornado.web.RequestHandler): def post(self): try: logging.debug("Got request: %s" % self.request.body) update = tornado.escape.json_decode(self.request.body) message = update['message'] text = message.get('text') if text: logging.info("MESSAGE\t%s\t%s" % (message['chat']['id'], text)) if text[0] == '/': command, *arguments = text.split(" ", 1) response = CMD.get(command, not_found)(arguments, message) logging.info("REPLY\t%s\t%s" % (message['chat']['id'], response)) send_reply(response) except Exception as e: logging.warning(str(e)) 

Here CMD is a dictionary of available commands, and send_reply is the function of sending a response, which accepts an already created Message object as input.

Actually, its code is quite simple:

 def send_reply(response): if 'text' in response: api.post(URL + "sendMessage", data=response) 


Now that the entire logic of the bot has been described, you can start inventing commands for it.

3. Teams


First of all, it is necessary to observe the Telegram agreement and teach the bot to two commands: / start and / help:

 def help_message(arguments, message): response = {'chat_id': message['chat']['id']} result = ["Hey, %s!" % message["from"].get("first_name"), "\rI can accept only these commands:"] for command in CMD: result.append(command) response['text'] = "\n\t".join(result) return response 


The message ['from'] structure is an object of type User , it provides the bot with information about both the user's id and the name of the user. For answers, it is more useful to use message ['chat'] ['id'] - in the case of personal communication there will be a User, and in the case of a chat - a chat id. Otherwise, you can get a situation where the user writes in the chat, and the bot responds in lichku.

The / start command without parameters is used to display information about the bot, and with parameters it is used for identification. It is useful to use it for actions that require authorization.

After that, you can add some of your own command, for example, / base64:

 def base64_decode(arguments, message): response = {'chat_id': message['chat']['id']} try: response['text'] = b64decode(" ".join(arguments).encode("utf8")) except: response['text'] = "Can't decode it" finally: return response 


For users of Mobile Telegram, it will be useful to tell @BotFather which commands our bot accepts:
I: /setcommands
BotFather : Choose a bot to change the list of commands.
I: @******_bot
BotFather: OK. Send me a list of commands for your bot. Please use this format:

command1 - Description
command2 - Another description
I:
whoisyourdaddy - Information about author
base64 - Base64 decode
BotFather: Success! Command list updated. /help


With such a description, if the user dials /, Telegram will helpfully show a list of all available commands.

4. Freedom


As you can see, Telegram sends the entire message, not a broken one, and the restriction that commands begin with a slash is for the convenience of mobile users only. Thanks to this, you can teach the bot to speak a little humanly.

UPD: As correctly suggested, this will take place only in person. In chat rooms only messages that start with the command (/ <command>) are delivered to the bot (https://core.telegram.org/bots#privacy-mode)
  • All messages that start with a slash '/' (see Commands above)
  • Messages that mention the bot by username
  • Replies to the bot's own messages
  • Service messages (people added or removed from the group, etc.)



In order for the bot to receive all the messages in the groups, we write the @BotFather command / setprivacy and turn off privacy.

First, add a handler to the Handler:

 if text[0] == '/': ... else: response = CMD["<speech>"](message) logging.info("REPLY\t%s\t%s" % (message['chat']['id'], response)) send_reply(response) 

And then we add pseudo-speech to the list of commands:

 RESPONSES = { "Hello": ["Hi there!", "Hi!", "Welcome!", "Hello, {name}!"], "Hi there": ["Hello!", "Hello, {name}!", "Hi!", "Welcome!"], "Hi!": ["Hi there!", "Hello, {name}!", "Welcome!", "Hello!"], "Welcome": ["Hi there!", "Hi!", "Hello!", "Hello, {name}!",], } def human_response(message): leven = fuzzywuzzy.process.extract(message.get("text", ""), RESPONSES.keys(), limit=1)[0] response = {'chat_id': message['chat']['id']} if leven[1] < 75: response['text'] = "I can not understand you" else: response['text'] = random.choice(RESPONSES.get(leven[0])).format_map( {'name': message["from"].get("first_name", "")} ) return response 

Here, the empirical constant 75 reflects relatively well the probability that the user still wanted to say. And format_map is convenient for the same description of strings as requiring substitution, and without it. Now the bot will respond to greetings and sometimes even address by name.

5. Not text.


Bots, like any normal Telegram user, can not only write messages, but also share pictures, music, stickers.

For example, expand the RESPONSES dictionary:

 RESPONSES["What time is it?"] = ["<at_sticker>", "{date} UTC"] 

And we will catch the text <at_sticker>:

 if response['text'] == "<at_sticker>": response['sticker'] = "BQADAgADeAcAAlOx9wOjY2jpAAHq9DUC" del response['text'] 

You can see that the Message structure now no longer contains text, so you need to modify send_reply:

 def send_reply(response): if 'sticker' in response: api.post(URL + "sendSticker", data=response) elif 'text' in response: api.post(URL + "sendMessage", data=response) 

And that's all, now the bot will occasionally send a sticker instead of time:



6. Opportunities


Thanks to the convenience of the API and a quick start, Telegram bots can be a good platform for automating their actions, setting up notifications, creating quizzes and task-based competitions (CTF, DozoR and others).

Recalling the article about the smart home , I can say that now there are fewer perversions, and the work is more transparent.

7. Limitations


Unfortunately, at the moment there is a restriction on the use of webHook - it works only on https and only with a valid certificate, which, for example, is still critical for me due to the lack of support from certification centers for dynamic DNS.

Fortunately, Telegram is also able to work on manual update, so without changing the code, you can create another service Puller, which will download them and send to the local address:

 while True: r = requests.get(URL + "?offset=%s" % (last + 1)) if r.status_code == 200: for message in r.json()["result"]: last = int(message["update_id"]) requests.post("http://localhost:8888/", data=json.dumps(message), headers={'Content-type': 'application/json', 'Accept': 'text/plain'} ) else: logging.warning("FAIL " + r.text) time.sleep(3) 


PS Under paragraph 7, I found a convenient solution - placing the bot is not at home, but on heroku, since all names of the form * .herokuapp.com are protected by their own certificate.

UPD: Telegram improved Bot Api, which is why it is not necessary now to have a separate function to send messages when the webhuk is set, and in response to a POST request, you can respond with the same generated JSON with a response message, where one of the fields is set as h ':' sendMessage '(or any other method used by the bot).

Source: https://habr.com/ru/post/262247/


All Articles