Progrobot: бот справки по языкам программирования

    Когда пишешь код, регулярно бывает нужно посмотреть справку по конкретной функции, модулю и т.д. Обычно я для этого захожу на cppreference.com или на docs.python.org, но это обычно не мгновенно — требует перехода по нескольким страницам минимум, а в питоновской документации еще и зачастую просто сложно найти нужную информацию на странице, не говоря уж о том, что гугл часто направляет на документацию по второй версии, а не по третьей, и приходится вручную переключать.

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

    Так получился бот @Progrobot. Ему можно отправить название функции и получить ее краткое описание, можно послать название модуля (в питоне) или заголовочного файла (в c++) и получить список всех функций в этом модуле, и т.д. Пока есть справка по c++ (с cppreference) и python3 (с docs.python.org). Еще планировал сделать поиск по stackoverflow, но оказалось, что API-шный поиск работает плохо, да еще и есть жесткое ограничение на количество запросов — короче, пока отключил, потом, может быть, выкачаю offline-базу и допилю.

    Про собственно бота


    Данные храню в mongo, на каждый язык две таблицы. В первой — собственно справка по объектам (функциям, классам, модулям и т.д.):«каноническое» имя, ссылка на страничку, откуда взята документация, модуль (питоновский модуль или cpp-шный header), к которому относится объект, формат использования (usage), описание, список дочерних элементов (методов для класса и т.п.) и строку copyright. К каждому дочернему элементу хранится также его краткое описание, которое я брал как первое предложение описания этого элемента. (Причем детектирование первого предложения оказалось тоже не совсем уж простой задачей.)

    Во второй таблице храню индекс: для каждого объекта храню его возможные имена, например, для std::vector::push_back в индексе будет лежать «push_back», «push_back vector» и «push_back std vector», со ссылкой на справку в первой таблице. А именно, разбиваю полное название объекта на токены, беру все суффиксы получившегося списка и для каждого суффикса сортирую его токены по алфавиту. Для каждой строки в индексе может быть несколько документов (например, push_back есть не только в векторе).

    Теперь логика бота достаточно простая: разбиваем запрос на токены, сортируем их по алфавиту, и ищем в индексе соответствующую запись. Нашли — ура, не нашли — видимо, такого объекта нет. Если есть несколько соответствующих записей, то выбираем из них наиболее подходящую (я решил для простоты выбирать примерно ту, у которой «каноническое» имя содержит минимальное количество токенов, например, запрос «get» вернет std::get, а не какой-нибудь xml.etree.ElementTree.Element.get). Все вообще соответствующие записи можно просмотреть командой /list.

    В базе у меня хранятся описания в html, чтобы сохранить форматирование кода и т.п. Телеграм также позволяет использовать в сообщениях некоторое простое подмножество html, поэтому написал конвертор, который выкидывает все неподдерживаемые теги и расставляет в подходящим местах переводы строк. Из спецэффектов здесь — в описаниях встречались локальные ссылки (<a href=”#anchor”>). Я их оставлял, и все работало, просто такие ссылки не работали в клиенте телеграма, но и не страшно. В очередной день я обнаружил, что бот не может отправить почти ни одного сообщения. Видимо, телеграм добавил дополнительную проверку на корректность адресов в ссылках, и перестал пропускать локальные ссылки. Пришлось оставлять только ссылки с полноценным адресом.

    Еще пришлось немного повозиться из-за того, что в телеграме длина сообщения ограничена 4096 символами (саму константу с трудом нашел в документации по телеграму), а описания некоторых объектов оказываются больше. Добавил немного заумный код, разрезающий длинные сообщения на более короткие в подходящих местах, и команду /cont, чтобы получить продолжение. Из числа неожиданных приколов тут — я делал так, чтобы все скобки в отрезаемой части сообщения были сбалансированы. А потом наткнулся на питоновский модуль random, в описании которого есть фраза «...generates a random float uniformly in the semi-open range [0.0, 1.0)». Пришлось считать квадратные и круглые скобки эквивалентными.

    Про парсинг


    Парсить html с cppreference оказалось сплошным удовольствием. Одна страница на сущность, хороший текст в стиле именно что reference, адекватные классы и id у html-тегов, список дочерних объектов прямо на страничке, и т.д. Взял три странички в качестве примеров, написал довольно простой код с использованием BeautifulSoup, который хорошо парсил бы эти странички, и все заработало. Потом только подкручивал по мелочи; сейчас там еще есть некоторые шероховатости, которые руки не доходят исправить, но в общем и целом все работает. Из нетривиальных подкручиваний было наполнение описания и дочерних элементов для заголовочных файлов (чтобы по запросу «algorithm» можно было получить список всех функций в этом файле), а также более аккуратная обработка специализаций шаблонов (изначально std::vector у меня разбивался на токены std vector bool, в результате чего он находится просто по запросу bool; пришлось специализацию выкидывать перед токенизацией).

    А вот парсинг питоновской документации был намного веселее. Она написана как книга, которую можно читать подряд. В результате там перемешаны идеология, советы по использованию, примеры, и собственно нужная мне reference, а в довершение всего есть фразы-связки типа «The pprint module defines one class:», которые никак не отличишь от следовавшего выше описания самого модуля. Поэтому, после того, как все заработало на трех страничках-примерах, парсинг питоновской документации пришлось еще долго допиливать, да и сейчас еще есть проблем больше, чем с cpp. Например, эта фраза про pprint так и присутствует сейчас в ответе бота, и выглядит там странно.

    Из проблем, которые пришлось фиксить — описания ряда сущностей начинаются со слов «New in version x.x» или «Source code: …», а я брал первое предложение как краткое описание этой сущности. Не нашел решения лучше, чем просто захардкодить, что строки такого вида не могут быть кратким описанием. У декораторов приходилось местами обрезать символ @. Начало описания новой сущности определяется тегом, у которого есть класс «class» или «classmethod» или «exception» или что-то еще, всего 9 вариантов, и я далеко не сразу обнаружил их все (а в cpp каждый файл — это отдельная сущность, и проблемы нет). Некоторые сущности мои скрипт детектировал сразу в двух местах (модуль unittest.mock детектировался тут и тут). В текстах есть таблицы и прочие структуры, которые плохо переводятся в формат сообщения в телеграме (да и не хотелось бы их переводить), по таким структурам лидер — itertools, пришлось при обнаружении строки, которая полностью жирным шрифтом, считать, что описание закончилось. Наконец, на docs.python.org очень сложно понять, какая лицензия распространяется на собственно документацию; мне пришлось даже писать на docs@python.org. Зато здесь нет этих проблем со специализацией шаблонов, а также нет понятия “заголовочный файл” вообще — для каждого объекта однозначно и естественно определен “родительский”.

    Про фреймворк


    Чтобы не дергать Telegram API напрямую, я использую python framework для telegram-ботов telepot. Он много чего умеет, вплоть до поддержки бесед с пользователями, и писать на нем бота оказалось достаточно просто. Правда, он регулярно обновляется и имеет какое-то невообразимое количество вариантов использования, так что довольно сложно разобраться, какой вариант нужен в конкретном случае.

    Некоторой подставой оказалось то, что разные сообщения от телеграма имеют существенно разную структуру. У некоторых объектов есть просто поле id, у некоторых в названии поля также указано, чего это id (message_id или file_id). Или, например, у объекта Message есть поля chat и text, а у объекта CallbackQuery поля chat нет, а вместо поля text — поле data. Мне бы обрабатывать Message и Callback вообще одинаково, но это не получается, приходится дописывать мелкие хаки. Правда, писал я это в начале лета, а сам фреймворк активно летом дорабатывался, может быть, сейчас у них уже и лучше.

    Код


    Github: github.com/petr-kalinin/progrobot, код там довольно некрасивый — следствие моих многочисленных попыток разобраться с интерфейсом telepot’а.
    Поделиться публикацией
    Комментарии 30
      +1

      Попробуйте на замену telepot'у модуль pyTelegramBotAPI. Позволяет избавиться от кучи if-ов, заменив всё отдельными (и читаемыми!) декорированными функциями. Использую его с августа прошлого года, проблем не знаю.
      Пример кода, мне кажется, более "Pythonic-way"


      P.S. Бот интересный, спасибо. Фич-реквест: сразу выдавать ссылку на официальную документацию. Т.е., бегло ознакомился через бота — пошёл, прочитал на сайте.

        0
        Спасибо, но, кажется, он более низкоуровневый. telepot, например, умеет поддерживать нити беседы, просто создавая по объекту на нить. Например, я так обрабатываю команды /cont и /list. В pyTelegramBotAPI придется это делать вручную.
      0

      К парсингу питоновской доки: а не думали парсить их указатель (очень неплохо сгенерирован, в CHM искать материал достаточно просто).


      Здесь: https://docs.python.org/3/genindex-all.html

        0
        Не очень понял, что вы имели в виду про CHM, по ссылке я вижу обычный индекс. Я его действительно не видел, и, возможно, с ним было бы проще. Сейчас уже не будет проще, т.к. он дает только ссылки на разделы по каждой функции, а я их и так детектирую по тегам с нужными классами.
          0

          Я когда пользуюсь питонской докой в формате CHM, то именно этого индекса (точнее, его содержимого) в большой части ситуаций хватает, чтобы найти нужный раздел.


          Еще один вопрос: а можно ли ваш парсер использовать, чтобы документацию от других проектов из экосистемы питона искать?
          (в первую очередь интересуют NumPy / SciPy / Matplotlib)


          Как правило, для приготовления документации используется пакет Sphinx, поэтому теги (но и проблемы) будут более-менее одинаковыми.

            0
            Про другие проекты — я не пробовал, и вряд ли буду в ближайшее время. Если дойдут руки, я скорее попробую добавить документацию по другим языкам программирования типа JS или Java. Если хотите, можете попробовать :)
              +1
              Документация по NumPy, SciPy и Matplotlib есть в Zeal
                0

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

          +1
          https://kapeli.com/dash
            +1

            Ну и Zeal туда же.

              0
              Интересно, да, не знал. Преимущество телеграма — он у меня и так все время запущен.
              0
              Если я правильно понял, оно только под mac?
                0

                Чуть выше в другой ветке упоминался Zeal, аналог Dash как минимум под Win и Linux.

              0
              Спасибо! отличная вещь, как для дальнейшей доработки, так и для демонстрации возможностей бота!
                +4
                Отлично. Осталось только научить бот отдавать форматированный текст — и мы наконец-то получим веб как он задумывался, а не как его сломали.
                  0
                  Неужели это удобнее, чем открыть закладку в браузере? Понимаю, что прикольно разрабатывать ради разработки, но у меня в закладках на тулбаре браузера ссылки на документацию по языкам и API, которые я использую.

                  Самые часто используемые лежат на диске — благо, места по нынешним меркам занимают немного — чтобы, когда вдруг нет интернета или он медленный (через 3G на даче или в дороге), не страдать…

                  Хоть убей, не могу понять, чем удобнее бот.
                    0
                    К тому же, сейчас столько способов для создания оффлайн-сайтов.
                      0
                      Все-таки даже с закладкой вам потребуется несколько действий, чтобы найти нужную справку. Как минимум задать поиск на сайте и т.д. Это не долго, конечно, но меня даже лишние секунд 5-10 раздражают, когда погружен в написание кода.
                        0
                        Никто не мешает передать такую же строку прямо на сайт, и пусть веб сервер парсит её точно также как это делает бот. Можно даже алиас для curl написать в командной строке.
                          0
                          Сайты в той версии, как они сейчас есть, не очень для этого подходят, это надо отдельный сайт писать, или делать что-то аналогичное боту, но локальное. Мне показалось, бот проще.
                            0
                            HTTP бот ещё проще. Это крайне отработанная технология с большим количеством готовых библиотек. Главное не забывать, что тебе не нужны модные фреймворки, ангулар, и вообще java script. Просто: запрос — ответ.

                            HTTP хорош тем, что WebView — стандартный компонент и есть много в каких редакторах. Так что можно просто повесить обращение к боту на горячую клавишу. А ещё есть минималистичные браузеры, как например luakit, в которых обращение с запросом к боту документации тоже легко повесить на горячую клавишу. Он, конечно, уже мёртв, и всяких современных фич не поддерживает, но ведь для бота не нужен webgl, достаточно старого доброго html, а его уж поддерживают все браузеры, даже старые и мёртвые.
                      –1
                      Чего только не напридумывают, чтобы не пользоваться IDE :)
                        –1
                        А Golang добавить в список языков не планируете?
                          0
                          Вряд ли, во всяком случае далеко не в ближайшее время, тем более что я его вообще не знаю. Если и буду добавлять какие-то еще языки, то в первую очередь Java и JavaScript. С другой стороны, pull requests are welcome :)
                          0
                          … а можно просто скачать доки и добавить их в IDE
                            0
                            Что Вы имеете ввиду? Мне, например, не хватает доков в IDE. И в spyder понравился способ отображения доков, я бы такое в PyCharm тоже использовал.
                              0
                              В PyCharm есть Ctrl-Q (Quick Documentation View), очень похоже на Spyder

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

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