Telegram-бот для Redmine. Как упростить жизнь себе и людям

    В любой компании, использующей систему управления проектами и задачами, рано или поздно возникает желание объединить её с каким-нибудь популярным мессенжером для упрощения коммуникаций. Особенно если через эту систему идёт взаимодействие с клиентами.

    В статье речь пойдёт о том, как подружить Redmine с Telegram и при этом не поломать имеющиеся бизнес-процессы.



    Небольшая предыстория, как родилась идея сабжа.

    Наша компания занимается администрированием серверов и сайтов 24/7, развитием инфраструктуры для крупных и развивающихся интернет-проектов, проектированием отказоустойчивых систем и автоматизацией DevOps.

    Для взаимодействия с клиентами мы используем Redmine, который мы затюнили под наши процессы и навешали на него ряд приятных мелочей, которые делают нашу жизнь легче.

    Ещё когда мы только начали, было это в 2011, то решили, что будем вести коммуникации с клиентами только через наш таск-трекер. Это связано с тем, что помимо поддержки серверов и реакции на инциденты, бо́льшую часть нашей работы составляют задачи по развитию инфраструктуры. Нам важно в любой момент хорошо понимать текущее состояние инфраструктуры, вектор развития проекта, правильно оценивать потребности и узкие места, предлагать решения, согласовывать их и внедрять. Дело осложняется тем, что проектов много, над каждым работает не один человек, а команда администраторов. При этом администраторы работают посменно, часть из них могут не всегда пересекаться друг с другом в офисе, а задачи нужно делать. Поэтому для нас крайне важен вопрос синхронизации знаний и действий внутри команды и между сменами.

    Учитывая все эти факторы, мы решили, что единственный возможный способ держать ситуацию под контролем — сделать так, чтобы все коммуникации и вся работа проходила в одном месте — в системе задач. Естественно, мы ведём проектные wiki и делаем ещё много чего, чтобы решить проблему синхронизации действий и контекста, но ключевым моментом всегда была единая точка взаимодействия с клиентами. Если добавить к коммуникациям ещё какой-то канал, например, мессенджеры, скажем, для быстрого обсуждения каких-то вопросов, клиенты 100% начнут этим злоупотреблять, мы быстро потонем в пучине бесконечных комментариев, и важная информация начнёт просто теряться. Да и администраторов перспектива хаотичного общения с клиентами в 5 разных окнах против разложенной на чёткие бизнес-процессы работы по задачам в Redmine, мягко говоря, не привлекала.

    В итоге так мы прожили лет 5. За это время многие клиенты полюбили наш Redmine, его простоту и функциональность. Кто-то, поработав с ним у нас, решил внедрить его у себя в компании. Лишь изредка единичные клиенты интересовались, можно ли ставить нам задачи не только через систему задач, но и через какой-нибудь мессенджер, потому что так было бы удобнее.

    Но за последние 2 года число таких запросов значительно возросло. Настолько, что уже нельзя было не принимать их во внимание. И мы в очередной раз задумались, что нам с этим делать. Менеджеры по продажам, как обычно, пытались протащить идею каким-нибудь чудесным образом встроить общение с клиентами через Skype, WhatsApp или Telegram в наши рабочие процессы. Администраторы наотрез этого не хотели.

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

    Накидали небольшой план — сначала минимальный функционал, потом приятные мелочи. Через некоторое время мы получили прообраз будущего бота, который успешно доставил написанное ему сообщение из Telegram в Redmine, и наоборот! Это дало уверенность, что идея жизнеспособная. И мы приложили все силы к тому, чтобы довести её до конца и запустить.

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

    В конце статьи приведены ссылки где скачать и как настроить нашего бота.

    Итак, что у нас получилось?

    Авторизация пользователя


    Во-первых, чтобы пользователь мог работать с ботом — его нужно авторизовать.
    Для этого у пользователя должен быть активный Redmine-аккаунт.

    Ему необходимо придумать себе username и прописать его в настройках своего Telegram-аккаунта:



    Этот же username нужно прописать в настройках Redmine-аккаунта в специальное дополнительное поле «Telegram»:



    Далее пользователь находит бота в Telegram и нажимает Start. Бот авторизует его и приветствует:



    Постановка задач


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



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

    При выборе пункта «Добавить в последнюю задачу» комментарий пользователя добавится к задаче, по которой велась последняя переписка. Бот показывает её выше.

    Если выбрать пункт «Создать новую задачу», бот перейдёт в режим создания новой задачи:



    И предложит пользователю:

    • Выбрать проект (опционально, если не устроил дефолтный):




    • Указать приоритет задачи (опционально, если не устроит дефолтный):



    Пользователь может пропустить эти шаги, если его устроят дефолтные значения, и сразу нажать «Создать задачу», потребуется только указать для неё заголовок:



    После этого бот уведомит клиента о создании задачи (ссылка на задачу кликабельна и по ней при необходимости сразу можно будет перейти в тикет):



    Которая в Redmine будет выглядеть следующим образом:



    Если же пользователь нажал на «Выбрать задачу» — бот предложит список открытых на текущий момент задач, в которые можно отправить написанный комментарий:



    При этом пользователь видит только те проекты и задачи, к которым он подключен в Redmine. Это относится ко всем действиям в боте.

    Переписка по задачам


    Бот поддерживает доставку нотификаций об изменениях в задачах. Например, наш администратор выполнил поставленную задачу и просит клиента проверить результат:



    Вот что получит клиент:



    Отвечать на задачи можно через Reply на соответствующее сообщение в Telegram. Это особенно удобно когда работа ведётся сразу по нескольким задачам (ответ будет попадать сразу в нужный тикет без дополнительных вопросов):



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

    Поддержка аттачей


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

    Допустим, система мониторинга сообщила нам о проблеме на одном из серверов. Администратор через Redmine создал задачу и прикрепил к ней график системы мониторинга. Клиенту придёт следующее сообщение:



    На которое он может ответить голосовым сообщением:



    Голосовое сообщение будет доставлено в задачу и прослушано администратором.

    Ограничения


    Конечно же, они есть. Мы столкнулись со следующими:

    1. Длинные сообщения. Telegram имеет ограничение на длину одного сообщения — 4096 символов. Изначально длинные комментарии из Redmine просто обрезались. Пришлось писать обработчик, который разбивал длинные комменты на части. Причём так, чтобы это было приятно глазу, то есть чтобы коммент не обрывался строго на 4096 символе на середине слова, а разбиение происходило аккуратно, на стыке фраз. Для клиентов неудобство заключается в том, что в этом случае будет приходить несколько сообщений.
    2. Форматирование. Форматирование в Redmine и в Telegram — это две разные вселенные. Но оно и понятно, у них и предназначение несколько разное. Форматирование Redmine отображается в Telegram в виде служебных символов, и порой это сильно не удобно читать. Пока мы так и не придумали как выводить их в нормальном виде, но не оставляем надежд устранить эту неприятность. Из Telegram в Redmine тоже не всё переносится. Например, если текст содержит определённые смайлики, то получив такое сообщение Redmine просто обрежет его. Это особенности структуры базы данных Redmine. У нас есть некоторые намётки решения этой проблемы и они корректно работают на тестовом стенде, но пока боимся применить их на боевой базе.
    3. Имена изображений. Если в Telegram сделать аттач изображения (именно изображения), то боту придёт файл с именем в формате file_xyz.jpg. Соответственно, и в Redmine он попадёт с таким именем. Тут мы пока сделать ничего не можем, поскольку бот сам получает файлы с такими именами.
    4. Интерфейс и все сообщения бота пока только на русском языке, но в одной из ближайших версий мы планируем добавить поддержку других языков.

    Статистика использования


    С момента запуска прошло чуть больше двух месяцев. Первые наблюдения:

    1. На текущий момент около 3,5% задач создаются и обновляются клиентами через бота.
    2. Как уже писал выше, бот нашёл применение не только среди клиентов, но и активно используется нами самими. Очень удобно получать нотификации по задачам (прилетают быстрее, чем в почту) и тут же на них отвечать. По ощущениям, с запуском бота у нас подросла скорость коммуникаций по внутренним задачам.

    Кратко о том как устроен наш бот


    • Он написан на C. Точнее на нашем фрэймворке, который написан на С
    • Использует два типа БД: Redis и MySQL
    • Нам потребовалось написать плагин для Redmine, который бы активировал механизм хуков при создании задач и комментариев
    • Легко реплицируется и масштабируется

    Итак, приступим…

    Принципиальная схема работы бота следующая:



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

    Теперь давайте добавим немного экшена и познакомимся с некоторыми особенностями доставки сообщений в Telegram!

    Telegram разбивает большие сообщения на несколько мелких. Эти сообщения приходят с некоторой задержкой. Все вложения доставляются в виде отдельных сообщений и иногда с большой задержкой (по мере загрузки их пользователем). Т.е. просто так брать и пересылать все получаемые сообщения нельзя, иначе мы заспамим пользователей и испортим себе карму.

    И что же делать? Мы решили создать для этого очереди сообщений и использовать для их хранения Redis. Каждое поступающее сообщение наш бот кладёт в очередь и обновляет время задержки на его обработку. Когда эта задержка истекла мы считаем, что сообщение сформировано полностью и теперь можно выбирать задачу и отправлять его в Redmine.

    Далее встаёт вопрос: а как же выбрать нужную задачу в Redmine? Ведь для того, чтобы боту понять что делать с полученными данными — ему надо спросить об этом пользователя, а это, как ни крути, ещё одно сообщение (даже если это нажатие кнопки) и его надо как-то связать с предыдущими действиями пользователя. В этом нам помогут сессии.

    Первое пришедшее сообщение от Telegram-аккаунта пользователя — порождает сессию, которая содержит:

    • ID пользователя с которым связана сессия
    • Тип сессии (т.е. её состояние)
    • Данные, с которыми надо что-то сделат

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

    Ещё одним важным составляющим элементом бота является кэширование. Наш Redmine состоит из достаточно большого числа проектов, пользователей и некоторых других данных. Боту достаточно часто требуется обращаться к этим данным и если он каждый раз будет ходить за ними в Redmine, то это приведёт к заметным задержкам при обработке запросов.
    У бота есть отдельный процесс, который занимается только тем, что периодически получает из Redmine данные, требуемые для остальных процессов, и сохраняет их в Redis.

    А что насчёт MySQL?

    Выше отмечалось, что кроме Redis бот использует ещё и MySQL. Конечно, все данные можно было бы хранить в Redis, но несмотря на наличие периодических дампов — данные всё же размещаются в памяти и неожиданный сбой в системе может привести к их потере, а среди них есть очень важная информация, к которой нужно относиться особенно трепетно. Чтобы лучше понимать о чём идёт речь — вспомним первую часть статьи. Там говорилось о том, что пользователь может не только выбирать из диалога с ботом как поступить с его сообщением (создать новую задачу или выбрать существующую), но и сделать ответ на сообщение. И бот должен понять, в какую задачу нужно его добавить.

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

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

    Далее, когда пользователь сделает reply на это сообщение в Telegram, бот получит номер «родительского» сообщения и по нему определит номер задачи Redmine, в которую нужно послать комментарий. Описанное соответствие также будет установлено и для этого нового сообщения, т.е. reply потом можно сделать и на него. Это же справедливо и для событий, поступающих из Redmine. Наши клиенты считают, что это очень удобный механизм и, судя по статистике, очень активно пользуются им.

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

    Другими важными типами данных, которые нельзя потерять (причём их ещё больше нельзя потерять, чем первые) — это соответствие ID пользователей Redmine и Telegram. Если всё же это произойдёт, то пользователи просто перестанут получать информацию о событиях, происходящих в Redmine до тех пор, пока что-то не напишут боту. А т.к. по опросам бота чуть ли не чаще используют как уведомлятор (на замену почты), то тут мы можем получить ещё большие неприятности.

    Чтобы не расстраивать пользователей — положили эти данные в менее быстрый, но более надёжный MySQL.

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

    Масштабирование и отказоустойчивость


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

    С появлением бота наш Redmine в некоторых случаях превратился в чат и интенсивность обмена сообщениями постоянно растёт. Некоторые наши клиенты так же хотят интегрировать его в своей инфраструктуре. А учитывая то, что наша разработка полностью open source — не известно какая ещё компания или команда администраторов/девелоперов пожелает им воспользоваться и не известно с какими нагрузками бот столкнётся. По этому мы ещё на стадии проработки проекта заложили в него возможность масштабирования.

    В нашем проекте масштабирование можно разделить на два класса:

    • Масштабирование баз данных
      Тут всё достаточно известно:

      • Для MySQL можно использовать Percona Cluster или аналоги
      • Для Redis — Redis Cluster
    • Масштабирование самого бота

    Чтобы понять, как можно масштабировать бота — давайте более подробно рассмотрим процесс приёма и обработки сообщений.

    Принципиальная схема следующая:



    Единственная задача процесса «Rest API» — это полученное от Redmine или Telegram сообщение положить в очередь (ra-queue) и ожидать следующих. Эту очередь постоянно мониторят процессы «Queue-workers» и при появлении в ней данных — считывают их.

    Сообщения от Redmine сразу обрабатываются и отправляются нужным пользователям в Telegram. С событиями от Telegram всё сложнее. Т.к. их сразу отправлять в Redmine нельзя (причины описаны выше), то они перекладываются в другую очередь, уже разбитую по пользователям. Тип сообщения определяет время, в течении которого мы ждём другие сообщения. Рано или поздно все эти сообщения считываются, проходят предварительную подготовку, тем или иным образом изменяют состояние сессии и могут быть отправлены в Redmine со всеми прикреплёнными файлами, которые скачиваются из Telegram на этом же шаге.

    Тут важно отметить, что благодаря такому подходу и имеющемуся механизму блокировок — обработка всегда происходит на одном конкретном узле. Именно это позволяет создать столько инстансов бота и с таким количеством процессов «Queue-workers», сколько это необходимо в рамках имеющейся нагрузки.

    При получении Redmine'ом сообщения срабатывает хук и это же сообщение отправляется обратно боту, после чего доставляется остальным Telegram-аккаунтам, подписанным на задачу.

    Таким образом, инфраструктуру бота в распределённом варианте можно представить так:



    Но всё описанное никак не ограничивает возможность использовать бота в обычном standalone варианте с одним сервером MySQL и обычным некластерным вариантом Redis.

    Как попробовать бота?


    Для демонстрации работы бота мы сделали demo-версию, доступную по адресу demo.nxs-chat.nixys.ru.

    Чтобы попробовать его в действии — необходимо выполнить несколько предварительных шагов:

    • В demo.nxs-chat.nixys.ru/account/register зарегистрируйте минимум 2 аккаунта (чтобы была возможность переписываться между ними) и заполните поле Telegram хотя бы для одного из них
    • Активируйте созданные аккаунты
    • Найдите бота @nixys_demo_chat_bot для каждого Telegram-аккаунта, указанного при регистрации, и нажмите кнопку Start.
      Убедитесь, что в настройках Telegram-аккаунтов заданы те же usernames, что и в настройках аккаунтов в demo.nxs-chat.nixys.ru
    • Создайте проект в Redmine и добавьте в него созданные аккаунты в роли «Project member»
    • В созданном проекте добавьте задачу и начните общение!


    Важное замечание! Бот будет отправлять сообщения в Telegram в следующих случаях:
    • Аккаунту, являющемуся автором задачи (при условии, что это не автор конкретного комментария)
    • Аккаунту, являющемуся исполнителем задачи (при условии, что это не автор конкретного комментария)
    • Аккаунтам, находящимся в списке наблюдателей в задаче (при условии, что это не автор конкретного комментария)

    Как получить бота?


    Бот является полностью открытым и его можно получить как в виде исходных кодов, так и в виде пакета (пакеты пока доступны только для Debian 8, но в ближайшее время появится для Debian 9 и CentOS 7).

    Ссылка на Github репозиторий с исходными кодами бота. Там же находится инструкция по установке бота из пакетов и его настройке.

    Заключение


    В ближайшем будущем планируем добавить следующий функционал:

    • Упрощение создания задач
    • Настройка уведомлений
    • Мультиязычный интерфейс
    • Подписка на задачи и отписка от них через бота (сейчас это возможно сделать только через Redmine)
    • В перспективе планируем добавить распознавание голосовых файлов в текст, что будет весьма полезно для задач, которые клиенты создают голосом
    • И многое другое
    Nixys
    30,00
    Компания
    Поделиться публикацией

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

      +3
      Telegram-бот для Redmine. Как упростить жизнь себе и людям
      Он написан на C. Точнее на нашем фрэймворке, который написан на С

      Ребята знают как упрощать жизнь.

        +1
        Каждая компания на хабре будет выпускать свой плагин для связи Redmine и Telegram
        https://habrahabr.ru/company/southbridge/blog/332292/

        PS. Вы не думайте, я не против большого разнообразия решений. Просто удивил первый пост компании о том, что уже было на хабре от другой компании, которая предоставляет такие же или похожие услуги. Поначалу подумал, что первая сменила название :)
          0
          Когда мы в компании осознали потребность в подобном ПО, то, разумеется, внимательно изучили существующие решения (в том числе и то, которое Вы приводите). И т.к. ни одно из них не предоставляло нужный нам функционал — мы решили разработать собственного бота, который бы максимально вписывался в имеющиеся у нас бизнес-процессы и логику работу.

          Я ни в коем случае не хочу сказать, что имеющиеся решения плохие, нет, просто они другие. Например, наш бот позволяет нашим клиентам общаться с тех. поддержкой вообще не используя Redmine, а администраторам общаться с клиентами не используя Telegram.
          0
          Ох, как круто, прямо то, о чем я мечтал, но C, вы серьезно?
            0
            Ох, как круто, прямо то, о чем я мечтал


            Спасибо, мы рады, что наша идея оказалась полезной для сообщества!

            но C, вы серьезно?


            Да, вполне. При выборе языка для разработки бота мы исходили из следующего:
            1. У нас уже был свой framework, на котором разработано большинство внутренних инструментов для компании и который показал себя с хорошей стороны при решении подобных задач.
            2. У нас есть разработчики на Си, которые знают этот framework

            Поэтому мы посчитали, что получить максимально качественный продукт в максимально короткие сроки мы можем только с помощью Си.

            У меня встречный вопрос: почему для Вас важно на каком языке разработан бот? Ведь для него создан пакет и есть репозиторий из которого его можно поставить одной командой, т.е. не надо ни компилировать программу вручную, ни разбирасться с тем как её установить. Можно просто брать и начинать пользоваться. Если, конечно, Вы не хотите дорабатывать бота под себя :).
              0
              Хочется быть уверенным, что я смогу его поддерживать, даже если разработчики откажутся от развития, плюс иметь возможность вносить модификации.
            0
            Подскажите, где взять auth_token? bot_api_key выдаётся при создании бота, а что такое auth_token? bot_api_addr должен быть api.telegram.org или по шаблону из документации api.telegram.org/bot/? Сам сервис у меня работает, выдаёт статистику, данные по обновлённым задачам в редмайне, но бот не реагирует ни на одну команду… Конфиг такой: yadi.sk/i/MoK6CXlX3Rnepn
              0
              Разобрался. Но в логе почему-то ошибка
              [nxs-chat-srv]: tlgrm request error: wrong Telegram response code (response code: 404, response body: "")
              не хватает полного url для того, чтобы понять проблему…
                0
                Пока ответил в личку.

                Если решаемый вопрос окажется общим — по итогам резюме будет либо представлено тут, либо скорректирована инструкция по настройке бота.
                  +1
                  Проблема заключалась в том, что при конфигурировании бота в значение опции telegram.bot_api_addr был добавлен лишний завершающий символ '/' (т.е. было указано: https://api.telegram.org/). Бот это не проверял и при конкатенации строк добавлял ещё один слэш, что не позволяло зарегистрировать Webhook в Telegram.

                  Сейчас недочёт устранён, в репозиторий выложены пакеты с новой версией бота.

              0
              Спасибо за интересное решение. У нас в планах тоже есть интеграция с Telegram, но чтобы встроить его в Redmine как мессенджер, чтобы пользователи могли общаться между собой.

              У меня вопрос, почему вы не используете плагины для упрощения работы с задачами и жизненными циклами задач?

              У вас все движения задачи происходят через стандартное редактирование задачи с отображением всех полей?
                0
                Спасибо за интересное решение. У нас в планах тоже есть интеграция с Telegram, но чтобы встроить его в Redmine как мессенджер, чтобы пользователи могли общаться между собой.


                Скажите, а где (например, в какой-то задаче) в Redmine Вы предполагаете сохранять лог общения пользователей в Telegram?

                У меня вопрос, почему вы не используете плагины для упрощения работы с задачами и жизненными циклами задач?


                Исходя из специфики нашей работы нам, в принципе, достаточно стандартных средств самого Redmine и тех процессов, которые мы написали с использованием этого плагина. Но если Вы дадите ссылки на полезные плагины — буду благодарен! :)

                У вас все движения задачи происходят через стандартное редактирование задачи с отображением всех полей?


                Не совсем. Есть некоторые отклонения от стандартного движения задачи в соответствии с созданными нами процессами с помощью упомянутого плагина. Например, автоматическая смена статуса задачи с «Ожидание клиента» на «Ответ клиента» когда в тикет отвечает клиент.

                А видимость полей тоже ограничена в зависимости от роли пользователя в проекте, чтобы клиентам не выводить лишнюю информацию, загромождающую экран.
                0
                Интересное решение, но вот реализация на С немного отталкивает.
                Мне кажется под такие решение очень хорошо заходит реализация на Golang.
                  0
                  Если бы мы начинали с чистого листа, то скорее всего выбрали бы именно Go (тут я с Вами согласен), но т.к. у нас уже были какие-то наработки и готовые инструменты, то решили остановиться именно на использовании Си.

                  Скажите, пожалуйста, а почему именно Вас отталкивает реализация на Си?
                  0
                  Интересная тема, спасибо.

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

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