12 Mar 20:32 avatar

Как создать боты в Telegram: инструкция

Образование и обучение: Как создать ботов в Telegram: инструкция

Бот для Telegram — это по-прежнему приложение, запущенное на вашей стороне и осуществляющее запросы к Telegram Bot API. Причем API довольное простое — бот обращается на определенный URL с параметрами, а Telegram отвечает JSON объектом.

Рассмотрим API на примере создания тривиального бота:

1. Регистрация


Прежде чем начинать разработку, бота необходимо зарегистрировать и получить его уникальный id, являющийся одновременно и токеном. Для этого в Telegram существует специальный бот — @BotFather.

Пишем ему /start и получаем список всех его команд. 
Первая и главная — /newbot — отправляем ему и бот просит придумать имя нашему новому боту. Единственное ограничение на имя — в конце оно должно оканчиваться на «bot». В случае успеха BotFather возвращает токен бота и ссылку для быстрого добавления бота в контакты, иначе придется поломать голову над именем.

Для начала работы этого уже достаточно. Особо педантичные могут уже здесь присвоить боту аватар, описание и приветственное сообщение.

Не забудьте проверить полученный токен с помощью ссылки api.telegram.org/bot<TOKEN>/getMe, говорят, не всегда работает с первого раза.

2. Программирование


Создавать бота буду на Python3, однако благодаря адекватности этого языка алгоритмы легко переносятся на любой другой.

Telegram позволяет не делать выгрузку сообщений вручную, а поставить webHook, и тогда они сами будут присылать каждое сообщение. Для Python, чтобы не заморачиваться с cgi и потоками, удобно использовать какой-нибудь реактор, поэтому я для реализации выбрал tornado.web. (для GAE удобно использовать связку Python2+Flask)

Каркас бота:

URL = «api.telegram.org/bot%s/» % BOT_TOKEN
MyURL = «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)


Здесь мы при запуске бота устанавливаем вебхук на наш адрес и отлавливаем сигнал выхода, чтобы вернуть поведение с ручной выгрузкой событий.


Приложение торнадо для обработки запросов принимает класс tornado.web.RequestHandler, в котором и будет логика бота.

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))


Здесь CMD — словарь доступных команд, а send_reply — функция отправки ответа, которая на вход принимает уже сформированный объект Message.

Собственно, её код довольно прост:

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


Теперь, когда вся логика бота описана можно начать придумывать ему команды.

3. Команды


Перво-наперво, необходимо соблюсти соглашение Telegram и научить бота двум командам: /start и /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


Структура message['from'] — это объект типа User, она предоставляет боту информацию как id пользователя, так и его имя. Для ответов же полезнее использовать message['chat']['id'] — в случае личного общения там будет User, а в случае чата — id чата. В противном случае можно получить ситуацию, когда пользователь пишет в чат, а бот отвечает в личку.

Команда /start без параметров предназначена для вывода информации о боте, а с параметрами — для идентификации. Полезно её использовать для действий, требующих авторизации.

После этого можно добавить какую-нибудь свою команду, например, /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


Для пользователей мобильного Telegram, будет полезно сказать @BotFather, какие команды принимает наш бот: 

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


C таким описанием, если пользователь наберет /, Telegram услужливо покажет список всех доступных команд.

4. Свобода


Как можно было заметить, Telegram присылает сообщение целиком, а не разбитое, и ограничение на то, что команды начинаются со слеша — только для удобства мобильных пользователей. Благодаря этому можно научить бота немного говорить по-человечески.

UPD: Как верно подсказали, такое пройдет только при личном общении. В чатах боту доставляются только сообщения, начинающиеся с команды (/<command>) (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.)



Чтобы бот получал все сообщения в группах пишем @BotFather команду /setprivacy и выключаем приватность. 

Для начала в Handler добавляем обработчик:

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


А потом в список команд добавляем псевдо-речь:

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


Здесь эмпирическая константа 75 относительно неплохо отражает вероятность того, что пользователь всё-таки хотел сказать. А format_map — удобна для одинакового описания строк как требующих подстановки, так и без нее. Теперь бот будет отвечать на приветствия и иногда даже обращаться по имени.

5. Не текст.


Боты, как и любой нормальный пользователь Telegram, могут не только писать сообщения, но и делиться картинками, музыкой, стикерами.

Для примера расширим словарь RESPONSES:

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

 

И будем отлавливать текст <at_sticker>:

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


Видно, что теперь структура Message уже не содержит текст, поэтому необходимо модифицировать 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)


И все, теперь бот будет время от времени присылать стикер вместо времени:

Образование и обучение: Как создать ботов в Telegram: инструкция

6. Возможности


Благодаря удобству API и быстрому старту боты Telegram могут стать хорошей платформой для автоматизации своих действий, настройки уведомлений, создания викторин и task-based соревнований (CTF, DozoR и прочие).

Вспоминая статью про умный дом, могу сказать, что теперь извращений меньше, а работа прозрачнее.

7. Ограничения


К сожалению, на данный момент существует ограничение на использование webHook — он работает только по https и только с валидным сертификатом, что, например для меня пока критично за счет отсутствия поддержки сертифицирующими центрами динамических днс. 

К счастью, Telegram также умеет работать и по ручному обновлению, поэтому не меняя кода можно создать еще одну службу Puller, которая будет выкачивать их и слать на локальный адрес:

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(«localhost:8888/»,
data=json.dumps(message),
headers={'Content-type': 'application/json',
'Accept': 'text/plain'}
)
else:
logging.warning(«FAIL » + r.text)
time.sleep(3)


P.S. По пункту 7 нашел удобное решение — размещение бота не у себя, а на heroku, благо все имена вида *.herokuapp.com защищены их собственным сертификатом.

UPD: Telegram улучшили Бот Апи, из-за чего, теперь не обязательно иметь отдельную функцию для отправки сообщений при установленном вебхуке, а в ответ на POST запрос можно отвечать тем же сформированным JSON с ответным сообщением, где одно из полей устанавливается как ч 'method': 'sendMessage' (или любой другой метод, используемый ботом).

habrahabr.ru

2 комментария

avatar

ИНТЕРЕСНЫЕ БОТЫ TELEGRAM

1. RubBot (@rubbot)
Бот, который показывает актуальный курс валют.

2. GitHub (@githubbot)
Соединяется с репозиторием GitHub для получения уведомлений о правках файлов в режиме реального времени.

3. MISIS Books (@misis_bot)
Книги библиотеки Национального исследовательского технологического университета «МИСиС» в вашем распоряжении. Укажите тему, и бот предложит вам один из вариантов поисковой выдачи.

4. Movie (@movie_bot)
Помогает найти фильм для просмотра. Определитесь с категорией, после чего выбирайте, нравятся или нет предложенные ботом варианты.

5. Wolfram Alpha (@wolframbot)
Находит ответы на математические задачи и вопросы с числовыми ответами в сервисе Wolfram Alpha.

6. BukToPuHa (@buktopuhabot)
Настоящая викторина с несколькими сотнями участников и вопросами на русском языке.

7. Hangbot (@hangbot)
Игра в «Виселицу» — у вас ограниченное число попыток, за которое вы должны успеть отгадать слово.

8. PollBot (@pollbot)
Предоставляет возможность создать голосование в чате с заданными вариантами ответа. По окончании предоставляет статистику.

9. NATIONAL GEOGRAPHIC (@nationalgeographic_bot)
Лучшие фотографии National Geographic со всего мира. Ежедневный дайджест или выбор снимков конкретного животного или тематики.

10. GetMusicBot (@GetMusicBot)
Этот бот ищет и скачивает музыку из SoundCloud и ролики с YouTube. Находка для музыкальных фанатов, которые не хотят покидать пределов мессенджера для того, чтобы найти и переслать любимую песню.

11. ImageSearch (@ImageSearchBot)
Находит любое изображение по вашему запросу. В опциях вы можете запросить качество лучше или похожие картинки.

12. Daily_Music (@Best_Music_bot)
Отправляет лучшие и самые популярные песни за день.

13. GlobalWeather (@GlobalWeatherbot)
Показывает текущую погоду в указанном городе.

14. Teflbot (@TeflBot)
Подтянет ваши знания английского языка благодаря полезным картинкам с идиомами и распространенными словами, аудиоурокам и учебным материалам.

avatar

Интересно, сложно

Оставить комментарий