Почему Swift — отличный выбор для чат-бота?
Когда речь заходит о создании чат-ботов, многие сразу думают о Python или JavaScript. И это логично: там куча готовых библиотек и примеров. Но что, если ваш стек — это экосистема Apple? Или вы хотите сделать бота, который будет работать прямо на сервере Vapor? А может, вам просто интересно попробовать Swift вне iOS-разработки?
Swift — это не только красивый синтаксис и безопасность типов. Это еще и молниеносная скорость, отличная работа с сетью и мощные инструменты для парсинга данных. В этой статье мы не будем писать очередного «привет-мир» бота. Мы шаг за шагом соберем полноценного Telegram-бота на Swift, который умеет отвечать на команды, обрабатывать текстовые сообщения и парсить JSON. Погнали!
Что нам понадобится?
Прежде чем нырять в код, давайте разберемся с инструментарием. Для нашего проекта мы будем использовать:
- Swift 5.9+ — последняя стабильная версия.
- Foundation — встроенная библиотека для работы с сетью и JSON.
- Telegram Bot API — REST API, которое нам нужно будет дергать.
- Библиотека SwiftBot — легковесная обертка для работы с Telegram (или напишем свой минимальный клиент).
Если вы хотите пойти по пути максимального контроля, мы будем использовать только стандартные средства Foundation. Никаких сторонних зависимостей — только чистый Swift.
Шаг 1: Регистрируем бота у BotFather
Это самый простой шаг. Открываем Telegram, находим BotFather, пишем /newbot и следуем инструкциям. На выходе вы получите токен — длинную строку вида 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11. Сохраните его в переменную окружения или конфиг-файл. Никогда не вшивайте токен прямо в код, если планируете выкладывать проект на GitHub.
Токен — это ключ доступа к вашему боту. Через него мы будем отправлять запросы к Telegram API.
Шаг 2: Создаем структуру для работы с API
Для начала нам нужно написать простой HTTP-клиент. Telegram API работает через GET и POST запросы. Мы будем использовать URLSession — стандартный инструмент Swift для работы с сетью.
Создаем базовый класс BotClient
import Foundation
class BotClient {
private let token: String
private let baseURL = "https://api.telegram.org/bot"
init(token: String) {
self.token = token
}
// Метод для отправки запросов
private func sendRequest(method: String, parameters: [String: Any] = [:], completion: @escaping (Data?, Error?) -> Void) {
let urlString = "\(baseURL)\(token)/\(method)"
guard let url = URL(string: urlString) else {
completion(nil, NSError(domain: "Invalid URL", code: -1))
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
if !parameters.isEmpty {
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters)
}
let task = URLSession.shared.dataTask(with: request) { data, response, error in
completion(data, error)
}
task.resume()
}
// Получаем обновления (новые сообщения)
func getUpdates(offset: Int? = nil, completion: @escaping ([Update]?) -> Void) {
var params: [String: Any] = [:]
if let offset = offset {
params["offset"] = offset
}
sendRequest(method: "getUpdates", parameters: params) { data, error in
guard let data = data else {
completion(nil)
return
}
do {
let response = try JSONDecoder().decode(TelegramResponse.self, from: data)
completion(response.result)
} catch {
print("Ошибка парсинга JSON: \(error)")
completion(nil)
}
}
}
// Отправляем сообщение
func sendMessage(chatId: Int, text: String, completion: @escaping (Bool) -> Void = { _ in }) {
let params: [String: Any] = [
"chat_id": chatId,
"text": text
]
sendRequest(method: "sendMessage", parameters: params) { data, error in
completion(error == nil)
}
}
}
В этом коде мы создали базовый клиент, который умеет отправлять POST-запросы с JSON-телом. sendRequest — универсальный метод, который мы будем использовать для всех вызовов API. getUpdates получает новые сообщения, а sendMessage отправляет ответ.
Шаг 3: Модели данных для парсинга JSON
Telegram API возвращает данные в формате JSON. Нам нужно создать структуры, которые Swift сможет автоматически парсить с помощью Codable. Это избавит нас от ручного разбора словарей.
import Foundation
// Модель ответа от Telegram
struct TelegramResponse: Codable {
let ok: Bool
let result: [Update]?
}
// Модель обновления (сообщения)
struct Update: Codable {
let updateId: Int
let message: Message?
enum CodingKeys: String, CodingKey {
case updateId = "update_id"
case message
}
}
// Модель сообщения
struct Message: Codable {
let messageId: Int
let from: User?
let chat: Chat
let text: String?
let date: Int
enum CodingKeys: String, CodingKey {
case messageId = "message_id"
case from, chat, text, date
}
}
// Модель пользователя
struct User: Codable {
let id: Int
let isBot: Bool
let firstName: String?
let username: String?
enum CodingKeys: String, CodingKey {
case id
case isBot = "is_bot"
case firstName = "first_name"
case username
}
}
// Модель чата
struct Chat: Codable {
let id: Int
let type: String
let firstName: String?
let username: String?
enum CodingKeys: String, CodingKey {
case id, type
case firstName = "first_name"
case username
}
}
Обратите внимание на CodingKeys. Telegram использует snake_case (например, update_id), а Swift предпочитает camelCase. С помощью CodingKeys мы указываем соответствие между ключами JSON и свойствами структур.
Шаг 4: Логика обработки команд
Теперь самое интересное — заставим бота реагировать на команды. Напишем простой обработчик, который будет отвечать на /start и /help, а также повторять за пользователем любой другой текст.
class BotHandler {
private let client: BotClient
init(client: BotClient) {
self.client = client
}
func handleUpdate(_ update: Update) {
guard let message = update.message,
let text = message.text else { return }
let chatId = message.chat.id
if text.hasPrefix("/") {
handleCommand(text, chatId: chatId)
} else {
handleText(text, chatId: chatId, from: message.from)
}
}
private func handleCommand(_ command: String, chatId: Int) {
switch command {
case "/start":
let welcomeText = "