company_banner

ICQ New: инструкция по разведению ботов



    Каждый раз, заходя в мессенджер, мы встречаем ботов в самых различных своих проявлениях. Одни рассказывают про погоду, другие разыгрывают бургеры, а третьи так и вообще кидают мемы под настроение. Наверняка у многих из вас проскакивала мысль: «А не сделать ли мне своего бота?». К сожалению, частенько такие мысли разбиваются о непонимание, как вообще сделать бота. Наверное, для этого нужно быть крутым айтишником и разбираться в миллионах технологий? На самом деле, нет. И сегодня мы попытаемся показать, что создание своего бота — процесс простой и понятный. Разберем полный цикл создания бота, от получения необходимых данных из мессенджера до написания кода и его запуска на сервере.

    Некоторое время назад в ICQ сильно обновилась платформа ботов. Она стала более дружелюбной, понятной и удобной. С помощью Python-библиотеки от разработчиков мы и будем создавать своего первого бота.

    Первым делом


    Для начала, нужно быть зарегистрированным в ICQ. Сделать это можно через приложение для мобильного телефона, компьютера или прямо из браузера в веб-версии.

    После регистрации можно приступать к заведению собственного бота в системе:

    1. Найти metabot в ICQ.
    2. Написать ему команду /newbot.
    3. Отправить имя для своего нового бота (должно оканчиваться на bot).

    После этого metabot пришлет данные вашего бота:

    • botId: уникальный номер бота;
    • nick: имя бота для поиска;
    • token: токен, который используется для сетевых запросов к серверу.

    Всё, бот создан. Его можно найти в поиске, написать ему сообщение. Теперь нам нужно сделать так, чтобы бот был активным и что-то отвечал.

    На страннице https://icq.com/botapi/#/ есть полное описание методов API. Они используются для взаимодействия с сервером. Разберем некоторые из них подробнее.

    Базовый метод — /events/get. Он используется для получения новых событий бота. Например, если кто-то написал боту, то это событие будет отдаваться при запросе /events/get. Давайте напишем боту какое-нибудь сообщение и проверим появление соответствующего события. Сделать это можно, например, в браузере. Для этого нужно перейти по адресу https://api.icq.net/bot/v1/events/get?token=Ваш токен&pollTime=1&lastEventId=0. Параметр pollTime отвечает за длительность удержания запроса сервером. Например, если ввести значение 60, то сервер в течение 60 секунд будет ждать событий для бота, а если их не будет за это время, то сервер вернет пустой массив событий. lastEventId отвечает за последнее обработанное событие. Другими словами, события со значениями меньше, чем переданное, будут отсечены.

    После перехода по этому адресу на экране появится что-то подобное:

    {
     "events": [
      {
        "eventId": 41,
         "payload": {
             "chat": {
               "chatId": "745294945",
                "type": "private"
             },
             "from": {
                    "firstName": "Andrey",
                    "lastName": "Shvedov",
                    "nick": "shvedoff",
                    "userId": "745294945"
             },
             "msgId": "6831171945581511151",
             "text": "а",
             "timestamp": 1590506161
         },
         "type": "newMessage"
      }
     ],
     "ok": true
    }

    Таким же образом можно отправить сообщение от имени бота с помощью метода /messages/sendText.

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

    Пишем код?


    Да, но для начала нам нужно подготовить для этого свой компьютер. Будем использовать Python третьей версии (скачайте версию для своей ОС здесь: https://www.python.org/downloads/ и менеджер пакетов pip здесь: https://pip.pypa.io/en/stable/installing/ ) Также, нужно установить библиотеку для работы с ботами:

    $ pip3 install --upgrade mailru-im-bot
    

    Вот теперь можно приступать.

    Пишем!


    В качестве примера будем писать игрового бота, который будет проверять знания простейшей математики. Работать он будет так: человек пишет боту какое-нибудь сообщение, тот представляется и предлагает поиграть. После того, как пользователь соглашается, бот генерирует простой математический пример и 4 варианта на выбор. Когда пользователь нажимает кнопку с выбранным ответом, бот отвечает, правильно или неправильно.

    Итак, бот в первую очередь должен получать события от сервера. Для этого нам нужно, помимо импортирования всех библиотек, создать объект класса Bot и запустить получение обновлений:

    import logging
    from bot.bot import Bot
    
    
    logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
                        datefmt='%Y.%m.%d %I:%M:%S %p', level=logging.DEBUG)
    
    TOKEN = "" #your token here
    
    def main():
        bot = Bot(token=TOKEN)
        bot.start_polling()
    
    
    if __name__ == '__main__':
        main()

    Здесь мы импортируем библиотеку для журналирования и запускаем ее. Также импортируем библиотеку для ICQ-ботов, задаем необходимый параметр token, создаем объект класса Bot и запускаем получение обновлений от сервера (bot.start_polling()).

    Сейчас наш бот может получать события, но не может их обрабатывать. Дополним бота, чтобы он мог отвечать на сообщения. Для этого добавим необходимые импорты:

    import json
    from bot.handler import MessageHandler
    

    json нужен для форматирования части сообщения, отвечающей за кнопки. MessageHandler нужен для добавления в код обработчика новых сообщений.

    Далее, нам нужна функция, которая будет составлять сообщение для ответа:

    def startup(bot, event):
        default_markup = [
            [{"text": "Начать!", "callbackData": "start"}]
            ]
    
        first_message_text = "Привет! Я - игровой бот. Начнем?"
        bot.send_text(chat_id=event.from_chat,
             text=first_message_text,
             inline_keyboard_markup=json.dumps(default_markup))

    Она принимает на вход созданного бота и событие нового сообщения. default_markup — матрица кнопок (объект «список», состоящий из массива строк кнопок. В нашем случае это единственная кнопка Начать!). first_message_text— строка, текст которой будет отправлен пользователю. Метод send_text принимает:

    • chat_id (поле из обрабатываемого события) — идентификатор чата, из которого было получено сообщение,
    • текст отправляемого сообщения,
    • и inline_keyboard_markup — наши кнопки.

    Теперь осталось описать вызов этой функции при получении сообщения. Для этого в библиотеке есть специальная конструкция:

    bot.dispatcher.add_handler(MessageHandler(callback=startup))
    

    Она позволяет при получении нового сообщения автоматически вызывать функцию startup() с необходимыми параметрами. Этот обработчик нужно поместить в функцию main(). У нас получился вот такой код, который позволяет отправлять стартовое сообщение при получении ботом любого сообщения:

    import logging
    import json
    from bot.bot import Bot
    from bot.handler import MessageHandler
    
    logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
                        datefmt='%Y.%m.%d %I:%M:%S %p', level=logging.DEBUG)
    
    TOKEN = «» #your token here
    
    def startup(bot, event):
        default_markup = [
            [{«text»: «Начать!», «callbackData»: «start»}]
            ]
        first_message_text = «Привет! Я — игровой бот. Начнем?»
        bot.send_text(chat_id=event.from_chat,
            text=first_message_text,
            inline_keyboard_markup=json.dumps(default_markup))
    
    def main():
        bot = Bot(token=TOKEN)
        bot.dispatcher.add_handler(MessageHandler(callback=startup))
        bot.start_polling()
    
    if __name__ == '__main__':
        main()
    

    Для пользователя это будет выглядеть так:


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

    [ [{«text»: «Начать!», «callbackData»: «start»}]]

    Помимо текста, который написан на кнопке, в сообщении есть поле callbackData. При нажатии на кнопку сервер генерирует событие нажатия на кнопку. В нём он передает это поле как идентификатор нажатой кнопки. То есть, по этой строке, полученной от сервера, мы можем судить, какую именно кнопку нажал пользователь. таким образом, дальше нам нужно добавить в бота логику обработки нажатия на кнопку. Для этого нам снова потребуется импортировать необходимые методы:

    from bot.handler import BotButtonCommandHandler
    from bot.filter import Filter
    

    Первая строка — обработчик события нажатия на кнопку, вторая — фильтр, помогающий понять, какая именно кнопка нажата. Также нам нужно описать логику ответа на нажатия кнопки:

    def start(bot, event):
        question = ''
        operands = ["+", "-", "*"]
        operations_count = randrange(3)+2
        for i in range(operations_count):
            question += str(randrange(9))
            question += choice(operands)
        question = question[:-1]
        answer = eval(question)
        buttons = [
            [{'text': str(answer), 'callbackData': "right"},
             {'text': str(answer+1), 'callbackData': "wrong"}],
            [{'text': str(answer-1), 'callbackData': "wrong"},
             {'text': str(answer+2), 'callbackData': "wrong"}]]
        shuffle(buttons[0])
        shuffle(buttons[1])
        shuffle(buttons)
        bot.answer_callback_query(
            query_id=event.data['queryId'],
            text='Новый вопрос')
        bot.send_text(chat_id=event.data['message']['chat']['chatId'],
            text=question,
            inline_keyboard_markup=json.dumps(buttons))
    

    По аналогии с сообщениями, функция принимает на вход такие же аргументы. Затем генерируется пример и массив кнопок с ответами. Один из ответов будет правильным. По этому признаку у ответов будут разные callbackData. В конце нужно обязательно вызвать метод answer_callback_query с параметрами:

    • query_id — уникальный номер события нажатия на кнопку,
    • и text — текст, который будет показан во всплывающей подсказке при нажатии на кнопку.

    С помощью вызова этого метода мы покажем пользователю, что бот принял нажатие на кнопку. Иначе на кнопке какое-то время будет отображаться индикатор загрузки. Еще с помощью метода send_text нужно отправить само сообщение с вопросом и кнопками ответов.

    Давайте сразу добавим и функции для обработки правильных и неправильных ответов:

    def right(bot, event):
        bot.send_text(chat_id=event.data['message']['chat']['chatId'],
            text='Правильно!')
        start(bot, event)
    
    
    def wrong(bot, event):
        bot.send_text(chat_id=event.data['message']['chat']['chatId'],
            text='Неправильно:(')
        start(bot, event)
    

    Эти функции будут вызываться при нажатии на правильный и неправильный ответы. И после сообщения с правильным ответом пользователь получит новый вопрос.

    Всё, осталось только добавить обработчики нажатия на кнопки:

        bot.dispatcher.add_handler(BotButtonCommandHandler(
            callback=start, filters=Filter.callback_data(«start»)))
        bot.dispatcher.add_handler(BotButtonCommandHandler(
            callback=wrong, filters=Filter.callback_data(«wrong»)))
        bot.dispatcher.add_handler(BotButtonCommandHandler(
            callback=right, filters=Filter.callback_data(«right»)))
    

    В каждом из этих трех обработчиков записан фильтр по callbackData (например, filters=Filter.callback_data(«start»)) и функция для вызова.

    На этом программирование бота завершено. Итоговый код выглядит так:

    import logging
    import json
    from bot.bot import Bot
    from bot.handler import BotButtonCommandHandler
    from bot.handler import MessageHandler
    from bot.filter import Filter
    from random import randrange, choice, random, shuffle
    
    logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
            datefmt='%Y.%m.%d %I:%M:%S %p', level=logging.DEBUG)
    
    TOKEN = "" #your token here
    
    
    def startup(bot, event):
        default_markup = [
            [{"text": "Начать!", "callbackData": "start"}]
            ]
    
        first_message_text = "Привет! Я - игровой бот. Начнем?"
        bot.send_text(chat_id=event.from_chat,
            text=first_message_text,
            inline_keyboard_markup=json.dumps(default_markup))
    
    
    def start(bot, event):
        question = ''
        operands = ["+", "-", "*"]
        operations_count = randrange(3)+2
        for i in range(operations_count):
            question += str(randrange(9))
            question += choice(operands)
        question = question[:-1]
        answer = eval(question)
        buttons = [
            [{'text': str(answer), 'callbackData': "right"},
             {'text': str(answer+1), 'callbackData': "wrong"}],
            [{'text': str(answer-1), 'callbackData': "wrong"},
             {'text': str(answer+2), 'callbackData': "wrong"}]]
        shuffle(buttons[0])
        shuffle(buttons[1])
        shuffle(buttons)
        bot.answer_callback_query(
            query_id=event.data['queryId'],
            text='Новый вопрос')
        bot.send_text(chat_id=event.data['message']['chat']['chatId'],
            text=question,
            inline_keyboard_markup=json.dumps(buttons))
    
    
    def right(bot, event):
        bot.send_text(chat_id=event.data['message']['chat']['chatId'],
            text='Правильно!')
        start(bot, event)
    
    
    def wrong(bot, event):
        bot.send_text(chat_id=event.data['message']['chat']['chatId'],
            text='Неправильно:(')
        start(bot, event)
    
    
    def main():
        bot = Bot(token=TOKEN)
        bot.dispatcher.add_handler(MessageHandler(callback=startup))
        bot.dispatcher.add_handler(BotButtonCommandHandler(
            callback=start, filters=Filter.callback_data("start")))
        bot.dispatcher.add_handler(BotButtonCommandHandler(
            callback=wrong, filters=Filter.callback_data("wrong")))
        bot.dispatcher.add_handler(BotButtonCommandHandler(
            callback=right, filters=Filter.callback_data("right")))
        bot.start_polling()
    
    
    if __name__ == '__main__':
        main()

    Замечу, что в нем присутствуют импорты, необходимые для работы функции генерирования вопроса. Вместо этой и других функций можно написать какую-то свою логику :)

    Где и как запускать?


    Бота удобнее всего запускать на удаленном сервере. Есть множество сервисов, которые предлагают виртуальные серверы с доступами, достаточными для установки программ. Некоторые сервисы предоставляют бесплатный тестовый период. Раз уж мы Mail.ru, то и разбирать будем на примере Mail.ru Cloud Solutions. Там простой процесс регистрации, быстрый доступ к своему серверу, а также бесплатный тестовый период без привязки банковской карты и прочих трудностей.

    1. Регистрируем аккаунт на https://mcs.mail.ru/.
    2. Создаем инстанс виртуальной машины. Все настройки оставляем без изменений, их достаточно.
    3. Сервис предложит скачать ключ доступа. Скачиваем.
    4. Теперь заходим на свою виртуальную машину: ssh -i ./xxx.pem user@ip

      где ./xxx.pem — путь до скачанного ключа.
    5. После этого нам нужно установить необходимые программы и пакеты:

      $ sudo curl https://raw.githubusercontent.com/dvershinin/apt-get-centos/master/apt-get.sh -o /usr/local/bin/apt-get
      $ chmod 0755 /usr/local/bin/apt-get
      $ sudo apt-get install python3
      $ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
      $ python3 get-pip.py
      $ pip3 install --upgrade mailru-im-bot

      Здесь мы установили Python и все необходимые библиотеки для работы нашего бота.
    6. Осталось скопировать бота на сервер и запустить его. Из папки со скриптом выполняем: scp -i ./xxx.pem ./path/to_bot.py user@ip:~

      • ./xxx.pem — путь до ключа;
      • ./path/to_bot.py — путь до файла с ботом;
      • user@ip:~ — логин, адрес и путь, куда копировать бота.

      После этого файл с ботом окажется в домашней директории на удаленном сервере.
    7. Заходим обратно на сервер и запускаем: python3 ~/file_name.py, где file_name.py — имя нашего файла с ботом.

    Что дальше?


    В результате у нас получился самостоятельно работающий бот, лежащий на нашем личном сервере. Если вы хотите дальше осваивать создание ботов для аськи, то подробнее изучите документацию по API ботов и усложните внутреннюю логику работы вашего бота. А еще в ICQ есть чат, в котором можно задавать вопросы по ботам: https://icq.im/botapi_faq.

    Полезные ссылки


    https://icq.com/botapi/#/ — ICQ bot API.

    https://github.com/mail-ru-im/bot-python — Python-библиотека для ICQ-ботов.

    https://icq.im/metabot — получить токен и отредактировать бота.

    https://github.com/shvedoff/Icq_buttons — бот, выполняющий все возможные действия с кнопками.

    https://icq.im/AoLE1J5Ecm5DIjZh1gg — чат для вопросов по ботам.
    Mail.ru Group
    Строим Интернет

    Комментарии 20

      +18
      «Закопайте стюардессу».
        –3

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

          –9
          «Закопайте стюардессу».
          Давно вы пользовались стюардессой? Аська нынче мало чем отличается от всяких богомерзких вайберов, псевдоудобных и псевдобезопасных телеграмов.

          Вопрос, почему следует хоронить аську, а не вайбер?
            –1
            потому что вайбер уже похоронен?
            а аську стоит хоронить так как есть телеграмм и вотсап.
              –3
              потому что вайбер уже похоронен?
              Вайбер похоронен? Да он стандарт де-факто среди пенсионеров и людей далеких от интернета.

              а аську стоит хоронить так как есть телеграмм и вотсап
              Я параллельно сижу в телеге и в аське, и современная аська в некоторых моментах даже удобнее, уж извините. Псевдобезопасность телеги создает иллюзию преимущества.
              +7

              Назовите его Мейл ру мессенджер — претензий не будет.
              А так действительно получается, что сначала старую аську гробили-гробили, в конце концов всё-таки убили, а теперь как будто в твои светлые воспоминания кто-то залез и перебирает там что-то. Неприятно просто.

                0
                Назовите его Мейл ру мессенджер — претензий не будет.
                Согласен. Новая аська со старой не имеет абсолютно ничего общего, кроме наличия UIN. Это два разных мессенджера. Но если отбросить «неприятно» и проблему названия, то новая аська объективно ничем не уступает своим конкурентам по соотношению плюсов и минусов.
                  +3
                  Наличия UIN тоже нет у тех, кто не заходил в аську больше года (пишу это на основании ответа из службы поддержки), а таких я думаю немало, учитывая состояние Аси в последнее время. Хотел было затестить новую асю, но не смог зайти и восстановить пароль. Оказалось, мой акк банально прибили. Так что, как говорят в таких случаях: умерла так умерла, закопайте обратно.
              +4
              Есть аська, и есть, что такого-то? Подход «мне не нужно, значит никому не нужно» не объективен. Я новой аськой пользовался раза три, не нравистя, что нигде не видно свой UIN. В профиле наткнулся на @цифры_uin, поменял на свой ник, а когда хотел поменять обратно на UIN, то оказалось, что с цифры начинать нельзя. Теперь моего UIN не видно вообще нигде.

              Да, теперь это какой-то другой мессенджер, в который взяли базу из старого и зачем-то оставили название.
              Но, например, когда телеграм только появился, лично я к нему относился точно так же: «Зачем ещё один мессенджер, для которого нужно ставить клиента на телефон, когда все чатики и в браузере хорошо работают?» Потом я обнаружил web-версию, пользуюсь ей, но не сказал бы, что лично мне телеграм намного удобнее, чем хотя бы IRC.
                0
                Не взяли, потому что аккаунты из старой базы убиты. Как бишь его там… «Астрологи объявили неделю прибитых аккаунтов: все владельцы «малознаков» поминают свои UIN'ы», во!
                +1
                С другой стороны… «Что мертво, умереть не может...» ;) Так что, почему бы и не?
                +1
                Либо плюсы тоже ставили боты, либо я единственный, кого при виде этого поста пробирает ощущение словно моего доброго дедушку откопали из могилы и пытаются научить танцевать джигу.
                  +5

                  Да это вообще другой менеджер даже идеологически. Мне кажется, те кто отвечает за проект никогда в той аське не сидели, и даже не загуглили в чем смысл названия ICQ. Во всяком случае, мне трудно понять, как иначе можно было сделать ICQ без поиска.
                  P. S. Для тех, кто не пользовался аськой поясню: смысл был в том, что можно было искать собеседника по анкете, этакая социальная сеть без "сети" (открытых списков друзей). То есть ты выбирал в форме например "Россия, [свой город], интересы: музыка металл" и получаешь список местных чуваков, которые им интересуются, и с которыми можно попробовать поболтать на эту тему. Именно в этом была основная фича.

                    +3
                    И эта фича перестала работать в тот момент, когда поиск начал выдавать одних ботов-проституток :(
                  +1
                  Минусующего бота хоть бы научили что-нибудь отвечать на комментарии. Все-таки большая компания, можете себе позволить :)
                    0

                    webhook отсутствует — не интересно

                      –1
                      Один вопрос — «Зачем?».
                        +1

                        Перефразируя одну восточную мудрость:


                        • ты хочешь icq чтобы писать ботов?
                        • будь проклят ты, если тебе понадобится такая возможность!
                          +5
                          Вот вы прямо не сдаетесь. Дело не в новизне или старости. Не в наличии ботов. Не в каких-то UX, UI и прочее. Дело в MRG.
                            +1
                            Говорят, что в РФ нет института репутации, но эти ребята смогли!

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

                          Самое читаемое