Как iOS 'ник Telegram бота писал, на Swift +6


Речь пойдет о Telegrammer, Telegram Bot фреймворк для Linux/macOS, полностью написанный на Swift 4.1



Экспозиция: Как же пришла в голову такая мысль?


Одному моему pet-project (iOS приложение, связанное с Telegram, но сейчас речь не о нем) требовался веб-интерфейс для контент-менеджера, чтобы создавать описание, проставлять теги и прочее. Так как приложение уже было идейно связано с Telegram, сразу пришла в голову мысль отправлять контент напрямую в мессенджер и там уже выполнять вышеописанные действия.


Отправлять такие данные, как оказалось, можно через ботов (как устроены боты в Telegram).


Завязка: Искал готовое решение, подходящего не нашел.


И тут все звезды выстроились в ряд, сейчас поймете о чем я...


Есть множество надежных, зарекомендовавших себя, Server Side фреймворков, на Java, Go, Python, PHP и других, которые уже позволяют создать бота, без лишних проблем. Но это же не про нас (свидетелей ИеговыApple), мы легких путей не ищем.


А что если, написать бота на Swift?


Совсем недавно (по меркам языков программирования), активно начало развиваться Server Side Swift community, и появились несколько фреймворков, таких как Vapor, Perfect, Kitura, которые успели обрести достаточную известность.


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


Чем крут SwiftNIO?

SwiftNIO is a cross-platform asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.


It's like Netty, but written for Swift.


SwiftNIO is fundamentally a low-level tool for building high-performance networking applications in Swift. It particularly targets those use-cases where using a "thread-per-connection" model of concurrency is inefficient or untenable. This is a common limitation when building servers that use a large number of relatively low-utilization connections, such as HTTP servers.


To achieve its goals SwiftNIO extensively uses "non-blocking I/O": hence the name! Non-blocking I/O differs from the more common blocking I/O model because the application does not wait for data to be sent to or received from the network: instead, SwiftNIO asks for the kernel to notify it when I/O operations can be performed without waiting.


SwiftNIO does not aim to provide high-level solutions like, for example, web frameworks do. Instead, SwiftNIO is focused on providing the low-level building blocks for these higher-level applications. When it comes to building a web application, most users will not want to use SwiftNIO directly: instead, they'll want to use one of the many great web frameworks available in the Swift ecosystem. Those web frameworks, however, may choose to use SwiftNIO under the covers to provide their networking support.


Что я рассматривал из готовых Telegram Bot Swift библиотек:


  1. zmeyc/telegram-bot-swift — Достаточно продвинутая библиотека, весь API Telegram Bot парсится с сайта, как методы, так и модели, продвинутый роутинг, работает как на macOS, так и на Linux.
    Минусы, связанные с тем очевидно, что писалась достаточно давно, и на тот момент, все фреймворки Apple (особенно Foundation) работали на Linux очень нестабильно:


    • Отправляет запросы через curl
    • На момент исследования как сама библиотека, так и зависимые библиотеки, не были портированы на swift 4
    • Не поддерживается WebHooks
    • Давно не обновляющийся репозиторий, как следствие, некоторые фишки обновленного Telegram Bot API не поддерживаются.

  2. FabrizioBrancati/SwiftyBot — Да простит меня автор, вообще не понимаю почему у этого бота больше всех звезд, под капотом устаревший Vapor 2.4.0, и одинокий main.swift, который просто демонстрирует простейшую работу с ботом. Никаких моделей, хелперов, очередей отправки, ничего. Ах нет! В отличие от конкурентов, поддерживает WebHooks.


  3. ShaneQi/ZEGBot — Совсем простенькая реализация, реализованы основные методы, модели и опять же только LongPolling.



Итак, решено, будем писать свой Фреймворк. Честно признаюсь, вдохновил меня Andrey Fidrya своим ботом, но я решил, что можно попробовать сделать лучше.


Кульминация: Добро пожаловать в мир бэкенда. SwiftNIO, спасибо за взорванный мозг.


Признаюсь, после iOS приложений, писать приложение для Server Side достаточно сложно, мыслить приходится по другому.


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


Я решил использовать:



По ходу дела были найдены некоторые баги в Vapor HTTPClient, успешно пофикшены, доработан HTTPServer, для работы с https "из коробки", без необходимости использовать nginx и подобных.


Что получилось:


  • API для бота проектировалось с сильной оглядкой на проверенного игрока в этой области python-telegram-bot
  • Также как и у zmeyc/telegram-bot-swift все модели и методы бота Telegram Bot API могут быть сгенерированы запуском скрипта.
  • Реализованы режимы Longpolling и WebHooks
  • Реализованы хендлеры: CommandHandler, CallbackQueryHandler, RegexpHandler, MessageHandler
  • Множество фильтров для Updates
  • Два простых бота, в качестве примера, стандартный EchoBot и HelloBot

Развязка: Пишем бота, общеполезного, пусть проверяет орфографию.


Бот: @yandex_spell_checker_bot
Исходный код: https://github.com/givip/YandexSpellCheckerBot


main.swift выглядит так:


import Foundation
import Telegrammer

///Получаем токен из enviroment variable (наиболее безопасно, рекомендуется)
guard let token = Enviroment.get("SPELL_CHECKER_BOT_TOKEN") else { exit(1) }

do {
    ///Создаем инстанс бота
    let bot = try Bot(token: token)

    ///Создаем диспетчер для бота
    let dispatcher = Dispatcher(bot: bot)

    ///Создаем контроллер проверки орфографии
    let controller = SpellCheckerController(bot: bot)

    ///Регистрируем хендлер для команды /start, приветствуем пользователя
    let commandHandler = CommandHandler(commands: ["/start"], callback: controller.start)
    dispatcher.add(handler: commandHandler)

    ///Регистрируем хендлер для приватных сообщений, которые собственно и будем проверять на орфографию
    let textHandler = MessageHandler(filters: .private, callback: controller.spellCheck)
    dispatcher.add(handler: textHandler)

    ///Регистрируем хендлер инлайн сообщений, отправленных из меню бота.
    let inlineHandler = CallbackQueryHandler(pattern: "\\w+", callback: controller.inline)
    dispatcher.add(handler: inlineHandler)

    ///Запускаем сервер бота в режиме Longpolling
    _ = try Updater(bot: bot, dispatcher: dispatcher).startLongpolling().wait()

} catch {
    print(error.localizedDescription)
}

Эпилог: iOS разработчики и не только, пользуйтесь!


Фреймворк находится на стадии beta, и любой желающий может использовать его для своих нужд.


После месяца тестирования и доработок, остались некоторые недочеты, хотелки и много enhancement issues, но тем не менее, "Проверка орфографии" крутится на Ubuntu достаточно давно.


Буду благодарен за любой фидбек, и накидывание на вентилятор.




К сожалению, не доступен сервер mySQL