Сучасна диджитал-освіта для дітей — безоплатне заняття в GoITeens ×
Mazda CX 30
×

Покрокова інструкція: як створити телеграм-бота на Ruby

Мене звуть Володимир, я QA Automation Engineer у компанії Matic. Люблю автоматизувати процеси й постійно пробую нові та цікаві технології. Найцікавіший проєкт, яким я займався останнім часом, — телеграм-бот мовою програмування Ruby. На цей час телеграм налічує понад 200 мільйонів активних користувачів зі всього світу, і все більше людей встановлюють телеграм собі на пристрої та користуються ним щоденно.

Якщо ви думаєте, що під капотом телеграм-бота знаходиться штучний інтелект чи інша надскладна технологія, то хочу вас запевнити, що все не так страшно. У цій статті я навчу вас писати власних ботів, які будуть показувати погоду в різних містах. Надалі ж ви зможете використати ці підходи для написання інших ботів, що нагадуватимуть вам про дні народження, показуватимуть затори на дорогах, надсилатимуть цікаві новини тощо. Тож перейдімо до роботи, бо хтозна, можливо, вам у майбутньому вдасться монетизувати свого бота.

Ця стаття розрахована на людей, що починають свій шлях в ІТ й хотіли б спробувати щось нове, поекспериментувати з технологіями, створити свій власний проєкт. Не переймайтеся, дуже багато часу це не забере. Орієнтовно 15 хвилин на прочитання і 30 — на реалізацію ідеї.

Для того, щоб створити цей телеграм-бот, нам треба:

  1. Установити Ruby:
  2. Установити одне із середовищ розробки (Atom, Sublime, Visual Code, RubyMine (його я й використовую), підійде навіть блокнот).
  3. Створити акаунт в Openweather Api.
  4. Акаунт у телеграмі.

Наш бот показуватиме погоду в різних містах на основі даних Оpenweather API. Я вибрав саме цей ресурс — він безплатний і простий у користуванні. Також, як ви вже могли здогадатися, для написання цього телеграм-бота ми використаємо мову Ruby: вона одна з найпростіших для сприйняття мов програмування, бо розроблена для людей, а не машин. Ця мова open-source, має велике ком’юніті, а отже більшість рішень уже є в інтернеті, тому не треба придумувати складні алгоритми.

Створення телеграм-бота

Нам треба створити телеграм-бота, звідки ми можемо взяти токен. Детальніше про те, що таке токен, можете прочитати на Вікіпедії — «Токен автентифікації».

Для цього в телеграмі знайдіть і додайте до своїх контактів @BotFather і введіть команду /start.

Тепер напишіть команду /newbot для створення нового бота.

Настав час придумати назву для вашого бота. Це може бути будь-яке ім’я, наприклад: Weather Bot, Super Mega Weather.

Далі придумайте username, за допомогою якого ми зможемо знаходити нашого бота. Username має закінчуватися словом bot (або Bot), це вимога BotFather.

За допомогою цього нікнейма можна знайти й додати вашого бота до своїх контактів. Збережіть токен, який видасть бот, бо він нам знадобиться згодом.

OpenWeather API

Наш бот інтегруватиметься з OpenWeather, але ви можете використовувати будь-який інший сервіс. Отже, щоб інтегруватися з OpenWeather, насамперед вам треба зареєструватися там та прочитати інструкцію з використання.

Реєструємося та створюємо токен для роботи. Цей токен матиме вигляд набору букв і цифр. Збережіть його також, він нам скоро знадобиться.

Зверніть увагу! Для Api calls через Openweather треба зачекати приблизно годину після реєстрації для того, щоб система додала вас і ваш токен для роботи.

Вітаю! Половину роботи вже зроблено! Це було не складно, погодьтеся, залишилося лише написати трішки коду.

Створення проєкту й встановлення бібліотек

Для початку відкрийте Термінал.

  • Windows: Запустіть Командний рядок (Win+R, введіть команду cmd).
  • macOS, Linux: Введіть у Spotlight слово terminal і відкрийте його.

Створіть новий проєкт. Якщо використовуєте Rubymine, то натисніть File -> New -> Project.

Перейдіть у теку з проєктом, використовуючи Термінал (для навігації використовуйте команду cd folder_name).

Установіть менеджер бібліотек для мови Ruby bundler командою в Терміналі: gem install bundler.

Детальніше про bundler можна почитати тут .

Створимо Gemfile в корені проєкту (без розширення). Цей файл допомагає легко зберігати й установлювати бібліотеки (Gems у мові Ruby). Для створення цього файлу використайте таку команду: bundle init.

Детальніше про Gemfile — тут.

Використаймо дві бібліотеки, а саме: telegram-bot-ruby — для написання телеграм-бота; rest-client — для api calls.

Тобто кінцевий вигляд нашого Gemfile буде такий:

source 'https://rubygems.org'

gem 'rest-client'
gem 'telegram-bot-ruby'

Для встановлення бібліотек, які ми прописали в Gemfile, використайте таку команду: bundle install. Ця команда встановить усі потрібні бібліотеки для роботи з Gemfile. У цей момент будуть додані дві бібліотеки, а саме: rest-client і telegram-bot-ruby.

Написання коду

Весь наш код поміститься в одному файлі. Створімо його та назвімо weather.rb.

Для початку додамо бібліотеку для роботи з API. Для цього в першому рядочку напишемо: require 'rest-client'.

Створюємо клас для роботи з погодою. Клас — це шаблон, з якого створюються інші об’єкти. Використовуємо таку назву класу:

class Weather
end

Додамо всередину класу наш OpenWeather токен, який ми дістали в попередньому пункті та лінку для API. Для цього оголосимо такі константи:

API_URL = 'api.openweathermap.org/data/2.5/'.freeze
APPID = 'YOUR_OPEN_API_APPID'.freeze

Константи — це значення, що не мінятимуться.

Додаємо до класу кілька методів. Метод — це набір операцій, що повертають значення.

Додамо конструктор. Конструктор — це набір команд, які виконуватимуться при створенні класу:

def initialize(city)
 @city = city
end

У нашому випадку при створенні класу ми створюватимемо об’єкт city (тобто назву міста).

Додамо акцесор. Акцесор — це метод, що дозволяє читати дані, в нашому випадку @city.

attr_reader :city

Додаємо повідомлення для користувача:

def form_message
 temperature.nil? ? 'City not found' : "In #{city} city today is #{temperature} celsius #{select_icon(temperature)}"
end

Не лякайтеся численних символів. У нашому випадку перевіряємо, чи є температура. Якщо її немає, виводимо повідомлення, що міста немає. Якщо вона є, ми повертаємо таке повідомлення: In <назва_міста> city today <температура> celsius <іконка погоди>, про яку поговоримо згодом>.

Далі інкапсулюватимемо, тобто писатимемо приватні методи. Інкапсуляція дозволяє приховати методи від використання поза межами цього класу, щоб обмежити доступ до них випадкових змін. Для цього напишемо:
private

Додамо лінк для перевірки погоди:

def weather_url
 "#{API_URL}/weather?q=#{city}&APPID=#{APPID}&units=metric"
end

Цей лінк складається з URL для API, назви міста, вашого токена, а також використання метричної системи числення.

Тепер додаємо запит (request) на сервер. Відповідь (response) повернеться у форматі JSON — найпопулярнішому форматі серед API. Перетворимо його на хеш методом JSON.parse:

def weather_response
 @response_body ||= RestClient.get(weather_url).body
 JSON(@response_body)
end

Тут ми посилаємо get запит з нашою згенерованою URL, звідки дістаємо body. Після цього конвертуємо його в JSON.

Дістанемо температуру з нашого response:

def temperature
 weather = weather_response
 return nil unless weather

 weather.dig('main', 'temp').to_i
end

Звідси, з попереднього методу ми дістали response і перевіряємо, чи він не пустий. Якщо він пустий — повертаємося з методу й вертаємо nil, тобто нічого.

Додамо ще головний візуальний елемент — іконки для певних градусів температури:

def weather_icons
 {
   40..49 => '🔥',
   30..39 => '☀️',
   20..29 => '🌤',
   10..19 => '⛅️',
   0 => '☁️',
   -10..-1 => '☃️',
   -20..-11 => '❄️'
 }
end

Ці іконки взяті з телеграму, але можете використовувати свої. Тут застосовується hash, який показує іконки відповідно до температури (хеш — це колекція, що має унікальний ключ і його значення).

Напишемо метод для вибору іконок:

def select_icon(temperature)
 icon = weather_icons.select { |ico| ico === temperature }.values.first
 icon = '✨' if icon.nil?
 icon
end

Тут усе просто — дістаємо температуру й шукаємо її іконку в нашому попередньому методі. Якщо не знайшли іконки — показуємо іконку із зірочками.

Кінцевий вигляд класу буде такий:

require 'rest-client'

class Weather
 API_URL = 'api.openweathermap.org/data/2.5/'.freeze
 APPID = 'YOUR_OPEN_API_APPID'.freeze

 def initialize(city)
   @city = city
 end

 attr_reader :city

 def form_message
   temperature.nil? ? 'City not found' : "In #{city} city today is #{temperature} celsius #{select_icon(temperature)}"
 end

 private

 def weather_url
   "#{API_URL}/weather?q=#{city}&APPID=#{APPID}&units=metric"
 end

 def weather_response
   @response_body ||= RestClient.get(weather_url).body
   JSON(@response_body)
 end

 def temperature
   weather = weather_response
   return nil unless weather

   weather.dig('main', 'temp').to_i
 end

 def weather_icons
   {
     40..49 => '🔥',
     30..39 => '☀️',
     20..29 => '🌤',
     10..19 => '⛅️',
     0 => '☁️',
     -10..-1 => '☃️',
     -20..-11 => '❄️'
   }
 end

 def select_icon(temperature)
   icon = weather_icons.select { |ico| ico === temperature }.values.first
   icon = '✨' if icon.nil?
   icon
 end
end

На цьому етапі ми можемо дістати JSON з даними, з яких ми дістанемо значення температури в певному місті, а також формуємо повідомлення.

Напишемо клас із нашим телеграм-ботом. Для цього творіть у корені проєкту новий файлик з назвою: telegram_bot.rb

Під’єднуємо бібліотеку з телеграм-ботом. Для цього нам треба написати:

require 'telegram/bot'
require_relative 'weather'

Ми також під’єднали наш клас Weather для того, щоб Ruby зміг його знайти.

Створюємо константу, щоб можна було записати наш телеграм-токен, який ми отримали раніше: TOKEN = 'YOUR_TELEGRAM_TOKEN'.freeze

Створимо метод для того, щоб запустити нашого бота:

def run
 bot.listen do |message|
   weather_message(message)
 rescue => e
   puts e.message
 end
end
Цей метод слухає команди користувача й реагує на команду з погодою, яку ми напишемо згодом. Також він надсилає повідомлення з помилкою, якщо раптом щось пішло не так.

Інкапсулюємо такі команди:
private

Додаємо метод для запуску бота з нашим токеном:

def bot
 Telegram::Bot::Client.run(TOKEN) { |bot| return bot }
end

Цей метод запускатиме бота через нашу бібліотеку.

А тепер створюємо метод для того, щоб можна було надсилати повідомлення з погодою:

def weather_message(message)
 return unless message.text.include? '/weather'

 send_message(message.chat.id, Weather.new(city_name(message.text)).form_message)
end
Цей метод викликатиметься, якщо повідомлення від користувача міститиме слово '/weather' і надсилатиме повідомлення з погодою в чат, з якого надійшло це повідомлення.

Створимо метод, щоб можна було розібрати те, що ввів користувач:

def city_name(text)
 text.gsub('/weather', '').strip.tr(' ', '+')
end

У нашому випадку стираємо /weather і забираємо пробіли, щоб замінити їх на +. Адже так працює апішка OpenWeather з пробілами (якщо вони є).

Створимо метод, щоб можна було надіслати повідомлення з погодою в певний чат з певним текстом.

def send_message(chat_id, message)
 bot.api.sendMessage(chat_id: chat_id, text: message)
end

Згрупуємо наші методи. Для цього напишемо такий код:

class TelegramBot
 # наш код
end

Створимо цей клас і запустимо нашого бота: TelegramBot.new.run.

У нашому випадку створюємо об’єкт класу й викликаємо з нього метод run.

Кінцевий вигляд буде такий:

require 'telegram/bot'
require_relative 'weather'

class TelegramBot
 TOKEN = 'YOUR_TELEGRAM_TOKEN'.freeze

 def run
   bot.listen do |message|
     weather_message(message)
   rescue => e
     puts e.message
   end
 end

 private

 def bot
   Telegram::Bot::Client.run(TOKEN) { |bot| return bot }
 end

 def weather_message(message)
   return unless message.text.include? '/weather'

   send_message(message.chat.id, Weather.new(city_name(message.text)).form_message)
 end

 def city_name(text)
   text.gsub('/weather', '').strip.tr(' ', '+')
 end

 def send_message(chat_id, message)
   bot.api.sendMessage(chat_id: chat_id, text: message)
 end
end

Залишилося лише створити клас для запуску нашого телеграм-бота. Почнемо зі створення нового класу, з якого запускатимемо нашого бота.

run_bot. rb

Сюди запишемо такий код:

require_relative 'telegram_bot'

TelegramBot.new.run

Бот готовий. Ви впоралися, з чим я вас вітаю! Залишилося тільки запустити нашого бота.

Для цього напишемо в Терміналі таку команду: ruby run_bot.rb.

Знаходимо й додаємо нашого телеграм-бота. Переходимо в наш канал з ботом і, використовуючи команду /weather <city_name>, запитуємо про погоду.

У відповідь ми отримаємо таке повідомлення:

Висновки

Отже, сьогодні ви познайомилися з Ruby, а також навчилися писати простенького телеграм-бота. Надіюся, ця стаття надихне вас написати його й додати новий функціонал, а можливо, зробити щось своє.

Дякую, що прочитали мою статтю та випробували свої можливості. Сподіваюся, вам було цікаво! Буду вдячний за фідбек і ваші запитання в коментарях.

Також можете знайти код на Github.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось1
До обраногоВ обраному7
LinkedIn

Схожі статті




9 коментарів

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

Для тих, хто в танку, а в чому перевага саме Ruby для написання таких ботів?

Простота та готова стаття :)
Сама мова рубі писалася для людей, а не для машин, тому більш проста для сприйняття.
Також має велике комюніті, а отже рішення більшості складних речей є в інтернеті

Добре, але для того, щоб не бути голослівним, потрібно показати рішення на інших мовах, а потім порівняти)

Погоджуюсь
на інших мовах не писав

Знайшов ось тут статтю на java:
habr.com/ru/post/432548

Ось тут на C#
aftamat4ik.ru/...​ishem-bota-telegram-na-c

на решта — можете пошукати самі :)

Если это первая попытка написать что-то осмысленное на руби, то зачёт. Но если честно, то код делает больно :)

Наскільки боляче по 10 бальній шкалі ?)

2.47 celsius? Это точно не ошибка?

Можливо ви праві і треба було повертати цілим числом
Таке значення повертає Openweather api
openweathermap.org/current

Большое спасибо, отличная статья! Очень легко читается. С удовольствием почитаю следующую статью по Ruby

Підписатись на коментарі