В чаті можна часто можна помітити подібні питання:
• «Як створити бота? Хочу почати з цього»,
• «Чому треба писати равлики над функціями?»
• «Що таке polling?»
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍1
🌍 i18n, l10n - мови у боті #цікаве
👉 i18n (internationalization) - процес, який надає можливість використовувати програмам кілька мов. l10n (localization) - більш конкретні дії, спрямовані на використання готових інструментів для роботи з мовами і адаптацію до мовних культур
📌 Обрано саме 18 і 10, бо це просто кількість літер між крайніми буквами (а повні слова всім лінь писати)
😀 І навіщо мені цим користуватись?
• багатьом людям приємніше працювати зі своєю рідною мовою, це покращує користувацький досвід
• знаючи те, які мови обирають користувачі можна більш точно показувати їм те, що треба саме нам (кому буде приємно бачити рекламу мовою, яку ми не знаємо?)
👀 Як цим користуватись?
1️⃣ Інструменти
Почнемо з того, що нам треба працювати саме з ботом. На даний момент є такі інструменти:
• вбудований aiogram.utils.i18n (на основі GNU gettext і Babel)
• неофіційна aiogram_i18n від активного учасника спільноти Patrick (підтримує GNU gettext, але пріоритет на Project Fluent)
Думаю, легше буде почати з aiogram_i18n, та і виглядає вона перспективніше.
📌 GNU gettext був створений у 1995 р, детальніше (багато цікавого загалом). Project Fluent - у 2019, це дослідницький проект Mozilla. Він був створений для вирішення багатьох проблем i18n/l10n, детальніше.
2️⃣ Як працює aiogram_i18n
Простими словами її роботу можна описати так:
• при отриманні апдейту бібліотека за допомогою менеджера отримує код мови користувача (en, uk)
• спеціальний мідлвар користується цим менеджером і за допомогою Dependency Injection передає екземпляр I18nContext як i18n (назву можна змінити) у ланцюг до інших мідлварів, що стоять нижче, фільтрів і нарешті хендлерів
• в екземплярі вже визначена мова (локаль) користувача, тому за допомогою I18nContext в хендлері можна діставати відповідні переклади чи змінювати локаль юзера за замовчуванням
3️⃣ Звідки і який менеджер бере локаль
Якщо у нас локалі знаходяться в БД, то треба створити менеджер самостійно як підклас
В ньому треба буде реалізувати два методи get_locale і set_locale. Проте необов'язково реалізовувати свій менеджер - у бібліотеці є готові рішення на основі Memory/Redis/FSM. Отже, задача менеджера допомагати мідлвару отримувати локаль тим способом який ми йому «показали»
4️⃣ get_locale і set_locale
• Що повинен повертати метод get_locale?
» Просто назву коду мови, наприклад "en" чи "de". Ці коди повинні бути назвами директорій з перекладами, про це пізніше.
• Для чого set_locale?
» Буде логічно, якщо все, що пов'язано з перекладом, буде виконувати I18nContext - в тому числі і зміну локалі юзера
Доволі простий приклад:
📌 Зверни увагу, що в set_locale перший позиційний аргумент це locale
5️⃣ Підключення та оформлення
• Імпортуємо і реєструємо I18nMiddleware
• В аргументах як ядро вказуємо FluentRuntimeCore (
• В аргументи мідлвара передаємо потрібний нам менеджер (чи самописний)
• Далі вказуємо директорії з перекладами. Їх приклади дивись в репозиторіях. Головне правильный шаблон шляху і розподілені .ftl файли
• Про синтаксис .ftl файлів читай тут
• Тепер використовуй у хендлерах i18n.get і якщо хочеш роби стаби
📌 LazyProxy - це фільтр, що сам дістає відповідний переклад і порівнює його із вхідним текстом
⭐️ aiogram_i18n
• багатьом людям приємніше працювати зі своєю рідною мовою, це покращує користувацький досвід
• знаючи те, які мови обирають користувачі можна більш точно показувати їм те, що треба саме нам (кому буде приємно бачити рекламу мовою, яку ми не знаємо?)
1️⃣ Інструменти
Почнемо з того, що нам треба працювати саме з ботом. На даний момент є такі інструменти:
• вбудований aiogram.utils.i18n (на основі GNU gettext і Babel)
• неофіційна aiogram_i18n від активного учасника спільноти Patrick (підтримує GNU gettext, але пріоритет на Project Fluent)
Думаю, легше буде почати з aiogram_i18n, та і виглядає вона перспективніше.
2️⃣ Як працює aiogram_i18n
Простими словами її роботу можна описати так:
• при отриманні апдейту бібліотека за допомогою менеджера отримує код мови користувача (en, uk)
• спеціальний мідлвар користується цим менеджером і за допомогою Dependency Injection передає екземпляр I18nContext як i18n (назву можна змінити) у ланцюг до інших мідлварів, що стоять нижче, фільтрів і нарешті хендлерів
• в екземплярі вже визначена мова (локаль) користувача, тому за допомогою I18nContext в хендлері можна діставати відповідні переклади чи змінювати локаль юзера за замовчуванням
3️⃣ Звідки і який менеджер бере локаль
Якщо у нас локалі знаходяться в БД, то треба створити менеджер самостійно як підклас
aiogram_i18n.managers.base.BaseManager
В ньому треба буде реалізувати два методи get_locale і set_locale. Проте необов'язково реалізовувати свій менеджер - у бібліотеці є готові рішення на основі Memory/Redis/FSM. Отже, задача менеджера допомагати мідлвару отримувати локаль тим способом який ми йому «показали»
4️⃣ get_locale і set_locale
• Що повинен повертати метод get_locale?
» Просто назву коду мови, наприклад "en" чи "de". Ці коди повинні бути назвами директорій з перекладами, про це пізніше.
• Для чого set_locale?
» Буде логічно, якщо все, що пов'язано з перекладом, буде виконувати I18nContext - в тому числі і зміну локалі юзера
Доволі простий приклад:
class i18nManager(BaseManager):
user_languages = {}
async def get_locale(self, event_from_user: User, db: Database) -> str:
try:
return self.user_languages[event_from_user.id]
except KeyError:
language = await db.get_user_language(event_from_user.id)
self.user_languages[event_from_user.id] = language
return language
async def set_locale(self, locale: str, event_from_user: User, db: Database) -> None:
await db.set_user_language(event_from_user.id, locale)
self.user_languages[event_from_user.id] = locale
5️⃣ Підключення та оформлення
• Імпортуємо і реєструємо I18nMiddleware
• В аргументах як ядро вказуємо FluentRuntimeCore (
pip install fluent.runtime). Я обрав це, бо зараз доступні 3 ядра, чув що FluentCompileCore хочуть прибрати, а GNU gettext є і в build-in• В аргументи мідлвара передаємо потрібний нам менеджер (чи самописний)
• Далі вказуємо директорії з перекладами. Їх приклади дивись в репозиторіях. Головне правильный шаблон шляху і розподілені .ftl файли
• Про синтаксис .ftl файлів читай тут
• Тепер використовуй у хендлерах i18n.get і якщо хочеш роби стаби
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6⚡1👍1🔥1
Якщо коротко - можна зберігати повідомлення будь-якого типу, кастомізувати, і за допомогою інлайн режиму надсилати їх у будь-які чати. Є пошук, приватність, мови
📃 Кому цікаво, які технології юзав:
• aiogram
• aiogram-dialog
• aiogram_i18n
• PostgreSQL
• psycopg3
• SQLAlchemy
• Alembic
• Redis
• Docker
🔌 Хостинг - https://www.mvps.net/?aff=30539 :D. Поки перша версія, треба тестити, буду радий почути фідбек
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
📥 Деплой ботів та SSH ключі #цікаве
👉 Деплой - процес розгортання веб-сервісу (в нашому випадку бота) в робочому оточенні (хостинг чи власна інфраструктура)
1️⃣ Різні методи та інструменти
На даний момент є дуже багато різних технологій для деплою і підтримки (CI/CD) програмного забезпечення. Виділяється з них Docker - дуже зручний інструмент для контейнеризації, він дозволяє один раз зібрати образ (image) і на його основі створювати контейнери - ізольовані (на рівні OC, а не hardware) середовища для роботи сервісів. Є параметри, щоб Docker сам запускав контейнери, якщо сервер перезавантажити, перевіряв їх на працездатність і не тільки.
Звичайною практикою буде побачити базу даних в одному контейнері, а основний сервіс в іншому + ще якісь додаткові контейнери - кожен відповідатиме за свою задачу.
Базові образи, наприклад Python, PostgreSQL є на DockerHub, тому якщо треба, докер сам їх завантажить для побудови image. Всі технології прив'язані до якоїсь OC, тому їх багато варіантів + різні версії + lite збірки - можна заплутатися. Але якщо треба - розберешся
2️⃣ Використання
• Встанови докер та запусти його як сервіс
• Напиши Dockerfile - в ньому будуть інструкції для побудови образу
• Якщо в боті є додаткові сервіси (такі як бд, редіс і т.д) створи docker-compose.yaml щоб оркеструвати одразу декількома контейнерами (перед цим прочитай про мережі)
• Дивись на приклади. Попрактикуйся з написанням образів та конфігів для компоузу, але головне розумій що робить та чи інша команда і коли що слід використовувати
• Поцікався багатоетапними збірками (коли образ використовує збудований в цьому ж Dockerfile образ)
• Дізнайся як відкривати порти. Це корисно, якщо треба взаємодіяти з сервісами не заходячи в сам контейнер. Наприклад за допомогою SSH тунелю можна локально в редакторі підключатись до БД в контейнері на сервері
• Подивись що таке профілі, корисна штука за допомогою якої можна вибірково вказати, які сервіси компоузом не треба запускати, якщо ти цього явно не вкажеш
📌 Збирати образи на сервері є поганою ідеєю, подивитись в сторону Docker Registry
😀 SSH ключі - шлях до безпечної праці з серверами
💭 Базова концепція - є клієнт і сервер. Ціль - безпечно обмінюватись даними. Клієнт генерує пару ключів - приватний і публічний. За допомогою базової аутентифікації паролем клієнт поміщає на сервер публічний ключ. Далі можна спробувати з'єднатися за допомогою утиліти ssh. Також на приватний ключ можна додатково накладати пароль. Приватний ключ ніколи, нікому не даємо.
1️⃣ Створення пари ключів
Для цього є різні утиліти. У мене наприклад є ssh-keygen. Команда генерації може виглядати так:
-t алгоритм генерації ключа
-b довжина ключів в байтах
-f шлях, куди помістити ключі
📌 Чому назва одна, ключів же 2? Справа в тому, що публічний ключ буде мати назву key_name.pub, а от приватний просто key_name
2️⃣ Організація ключів
💭 А якщо у нас буде багато серверів, при тому декілька різних ключів для кожного користувача на сервері, все пхати в .ssh?
Для цього в директорії .ssh можна створити файл config. Він допомагає встановити відповідність між ключами та серверами. Наприклад:
І так далі. Кожен блок відповідає за одну пару ключів.
• Host - IP серверу чи кастомне ім'я
• HostName - домен чи IP серверу
• User - аккаунт на сервері
• IdentityFile - шлях до публічного ключа
• IdentitiesOnly - не пробувати інші ключі
3️⃣ Перенесення ключів
💭 Створили, організували, як перенести їх на сервер?
Наприклад за допомогою scp чи ssh-copy-id. Приклад:
📌 Ключі буде перенесено в домашню директорію user!
Вводимо пароль і ключі вже на сервері у потрібному місці!
📌 Після встановлення SSH ключів часто прибирають аутентифікацію паролем
👩💻 Docker Guide
🔑 SSH keys
1️⃣ Різні методи та інструменти
На даний момент є дуже багато різних технологій для деплою і підтримки (CI/CD) програмного забезпечення. Виділяється з них Docker - дуже зручний інструмент для контейнеризації, він дозволяє один раз зібрати образ (image) і на його основі створювати контейнери - ізольовані (на рівні OC, а не hardware) середовища для роботи сервісів. Є параметри, щоб Docker сам запускав контейнери, якщо сервер перезавантажити, перевіряв їх на працездатність і не тільки.
Звичайною практикою буде побачити базу даних в одному контейнері, а основний сервіс в іншому + ще якісь додаткові контейнери - кожен відповідатиме за свою задачу.
Базові образи, наприклад Python, PostgreSQL є на DockerHub, тому якщо треба, докер сам їх завантажить для побудови image. Всі технології прив'язані до якоїсь OC, тому їх багато варіантів + різні версії + lite збірки - можна заплутатися. Але якщо треба - розберешся
2️⃣ Використання
• Встанови докер та запусти його як сервіс
• Напиши Dockerfile - в ньому будуть інструкції для побудови образу
• Якщо в боті є додаткові сервіси (такі як бд, редіс і т.д) створи docker-compose.yaml щоб оркеструвати одразу декількома контейнерами (перед цим прочитай про мережі)
• Дивись на приклади. Попрактикуйся з написанням образів та конфігів для компоузу, але головне розумій що робить та чи інша команда і коли що слід використовувати
• Поцікався багатоетапними збірками (коли образ використовує збудований в цьому ж Dockerfile образ)
• Дізнайся як відкривати порти. Це корисно, якщо треба взаємодіяти з сервісами не заходячи в сам контейнер. Наприклад за допомогою SSH тунелю можна локально в редакторі підключатись до БД в контейнері на сервері
• Подивись що таке профілі, корисна штука за допомогою якої можна вибірково вказати, які сервіси компоузом не треба запускати, якщо ти цього явно не вкажеш
1️⃣ Створення пари ключів
Для цього є різні утиліти. У мене наприклад є ssh-keygen. Команда генерації може виглядати так:
ssh-keygen -t rsa -b 2048 -f ~/.ssh/key_name
-t алгоритм генерації ключа
-b довжина ключів в байтах
-f шлях, куди помістити ключі
2️⃣ Організація ключів
Для цього в директорії .ssh можна створити файл config. Він допомагає встановити відповідність між ключами та серверами. Наприклад:
Host 123.45.67.89
HostName mybot.io
User admin1
IdentityFile ~/.ssh/server1/key.pub
IdentitiesOnly yes
Host myserver
HostName 132.89.46.72
User admin2
IdentityFile ~/.ssh/server2/key.pub
IdentitiesOnly yes
...
І так далі. Кожен блок відповідає за одну пару ключів.
• Host - IP серверу чи кастомне ім'я
• HostName - домен чи IP серверу
• User - аккаунт на сервері
• IdentityFile - шлях до публічного ключа
• IdentitiesOnly - не пробувати інші ключі
3️⃣ Перенесення ключів
Наприклад за допомогою scp чи ssh-copy-id. Приклад:
ssh-copy-id -i ~/.ssh/.../key.pub user@ip_address
Вводимо пароль і ключі вже на сервері у потрібному місці!
🔑 SSH keys
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🤪1
• Самопідписаний сертифікат треба для безоплатного вебхуку
• nginx буде отримувати POST запити на вебхук і пересилати їх на обробку до бота (схоже на reverse proxy server, але в цьому пості все на одному сервері)
• FastAPI для обробки апдейтів бота вже за допомогою Python
Для створення сертифікату будемо користуватись утилітою OpenSSL
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./cert.key -outform PEM -out ./cert.pem
• req - підкоманда для сертифікатів
• x509 - один із стандартів для сертифікатів
• days 365 - час життя сертифікату
• newkey rsa:2048 - створити ключ алгоритмом RSA довжиною 2048 біт
• outform PEM - необхідний формат генерації PEM
• keyout і out - куди покласти ключ і сертифікат
Для початку його треба встановити та запустити як сервіс. Приклад для Debian GNU/Linux
sudo apt-get update
sudo apt-get install nginx
sudo nginx -v
sudo systemctl enable nginx
sudo systemctl status nginx
• робота із сертифікатом
• блокування запитів не від Telegram
• пересилання запитів до FastAPI
server {
listen 443 ssl; # For HTTPS
server_name YOUR_IP;
ssl_certificate /.../certs/cert.pem;
ssl_certificate_key /.../certs/cert.key;
location / {
# Telegram IP ranges
allow 149.154.160.0/20;
allow 91.108.4.0/22;
# Enable local requests
allow 127.0.0.1;
allow ::1;
# Ignore other requests
deny all;
# Reverse proxy mechanism
proxy_pass http://YOUR_IP:BOT_PORT;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}sudo systemctl restart nginx
app = create_app(
bot=bot,
dispatcher=dispatcher,
)
app.add_event_handler('startup', on_startup)
app.add_event_handler('shutdown', on_shutdown)
Приклад встановлення вебхуку (в startup наприклад)
await bot.set_webhook(
getenv('WEBHOOK_URL'),
certificate=FSInputFile('.../cert.pem'),
secret_token=getenv('WEBHOOK_SECRET'),
allowed_updates=list(ALLOWED_UPDATES),
)
Ручний варіант встановлення вебхуку
curl -F "url=https://ip_address/webhook_path" -F "secret_token=<SECRET>" -F "certificate=@cert.pem" https://api.telegram.org/bot<TOKEN>/setWebhook
Перевірити локально, чи працює сертифікат
openssl s_client -connect ip_address:443
Переглянути запити до nginx
tail -f /var/log/nginx/access.log
Переглянути статус вебхуку
https://api.telegram.org/bot<TOKEN>/getWebhookInfo
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥4👍2🥰2💅1
👉 Телеграм зірки - валюта в телеграм, яку можна купувати за реальні гроші та витрачати в месенжері на різні речі
• Зручність - на відмінну від інших способів оплати, використання зірок набагато зручніше як для користувача так і для сервісу. Користувач може легко поповнити свій баланс зірок через банківську карту (якщо телеграм завантажено з офіційного сайту) чи у Play Market або App Store. Проведення оплати можна зробити в декілька дотиків
• Легка можливість повернення зірок назад на баланс клієнта за допомогою ідентифікатору транзакції
• За допомогою зірок можна оплачувати Telegram Ads та переводити їх в реальні гроші
• Всі цифрові товари, які можна придбати в твоєму сервісі повинні оплачуватись виключно за допомогою зірок (це не стосується фізичних товарів). Тобто, якщо ти продаєш цифрові товари іншими способами, то телеграм може частково (на окремих платформах) або повністю заблокувати доступ до твого сервісу. Теоретично, процес перевірки автоматизувати неможливо, тому це стосується тільки дуже великих проектів у яких є відповідні товари. Це означає, що маленькі сервіси можуть проводити оплату будь-яким способом, але я не рекомендую подібні способи
• Комісія при оплаті зірками від 30% і більше, що руйнує деякі бізнес моделі
• Твій сервіс зобов'язаний брати на себе відповідальність за обслуговування клієнтів і повністю сам вирішувати спірні моменти (зокрема мати команду /paysupport)
• На даний момент, ціна однієї зірки становить 0.013 USD
• Переведення зірок у реальні гроші буде доступно через 21 день після їх отримання сервісом. Це буде пов'язано з криптовалютою і такий механізм працюватиме на платформі Fragment. Поки що, реальних переведень зафіксовано не було, тому деталі невідомі
• Зірки мають час життя в 3 роки з дати їх отримання і якщо їх не витратити, то вони автоматично спишуться з балансу
pre_checkout_query в дозволені, бо він не встановлюється за замовчуванням⬇️ Простий приклад коду для створення чеку на оплату 5 зірок (або посилання)
from aiogram import Router, F, Bot
from aiogram.filters import Command
from aiogram.types import (
Message,
LabeledPrice,
PreCheckoutQuery,
)
router = Router()
@router.message(Command('start'))
async def create_invoice(msg: Message):
await msg.answer_invoice(
noscript="Title",
denoscription="Denoscription",
payload="payload",
currency="XTR", # XTR only, don't change
prices=[
LabeledPrice(label="label", amount=5), # 5 telegram stars
],
)
@router.pre_checkout_query()
async def checkout_handler(checkout_query: PreCheckoutQuery):
await checkout_query.answer(ok=True)
@router.message(F.successful_payment)
async def star_payment(msg: Message, bot: Bot):
await bot.refund_star_payment( # for testing auto-recovery of funds
msg.from_user.id,
msg.successful_payment.telegram_payment_charge_id,
)
# What actions, such as:
# - adding a transaction to the database
# - opening access to paid functions
await msg.answer(f"Your transaction id: {msg.successful_payment.telegram_payment_charge_id}")
🍐 Книга Груші, про платежі
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🦄1
about aiogram
Щоб використати бота, просто напиши його юзернейм у текстове поле
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👨💻2💅1
📡 Офіційний безкоштовний вебхук:
aiohttp, nginx, домен, Let's Encrypt #цікаве
☑️ Це cheatsheet для встановлення бота на вебхук із сертифікатом від Let's Encrypt на свій сервер + його автооновлення + безкоштовний домен
📌 Перед цим варто прочитати
• Що таке вебхук і які переваги він дає
• Пост про самопідписаний сертифікат
📚 Трохи теорії
🔒 Let's Encrypt - це безкоштовний, автоматизований і відкритий центр сертифікації, створений некомерційною організацією Internet Security Research Group (ISRG)
🤖 Certbot - це безкоштовна утиліта з відкритим вихідним кодом для автоматичного використання сертифікатів Let's Encrypt
🪩 FreeDNS - середовище, де інші програмісти можуть безкоштовно обмінюватися доменними іменами один з одним. З'явився у 2001 році, тому що його засновник, Джошуа Андерсон, хотів розважитися зі своїм хобі, пов'язаним з доменами
💠 aiohttp - асинхронний HTTP клієнт/сервер для asyncio та Python
📌 Переваги офіційного сертифіката над самопідписаним:
• твій сервер визнається всіма (нап. при переході на його адресу в браузері не буде ніяких попереджень про безпеку)
• зручне автооновлення
• при встановленні вебхуку не треба надсилати файл
⬇️ Загалом:
• За допомогою FreeDNS отримуємо безкоштовний домен (для Let's Encrypt необхідно мати домен)
• Сертифікат для вебхуку беремо в Let's Encrypt
• certbot буде автоматично оновлювати наш сертифікат
• nginx буде отримувати POST запити на вебхук і пересилати їх на обробку до бота
• aiohttp буде приймати апдейти бота вже за допомогою Python
📌 Для різноманіття цього разу візьмемо aiohttp замість FastAPI, бо він більш легший. Але якщо тобі треба будувати API, то залишити FastAPI буде гарним рішенням
✨ Отримання безкоштовного домену
💭 Такий безкоштовний домен підійде або для тестів або для не дуже великих проектів. Тому, якщо в тебе серйозний проект, краще придбати домен
• Зареєструйся на сайті
• Перейди в розділ субдоменів і натисни «Add a subdomain»
• Переконайся, що в полі «Type» написано А
• У полі «Domain» обери бажаний домен (якщо натиснути «Many many more available» -> «Share Domain Registry», то з'явиться більше варіантів)
Краще обирати не дуже популярні, пізніше скажу чому. Продовжимо:
• в поле «Subdomain» введи назву субдомену (твоя авторська назва), виглядати буде як subdomain.domain
• в поле «Destination» введи IP адресу свого серверу
• після цього підтверди створення субдомену
🤩 Готово, домен є. Далі йди до свого хостинг провайдера і заяви право на цей домен. Наприклад у MVPS на панелі керування сервером є таблиця, в колонку «rDNS» треба ввести домен (ще стаття є)
📌 Пам'ятай, що необхідно буде почекати декілька хвилин (але іноді годин) поки домен не розійдеться по мережі
📥 Отримання сертифікату
Далі зайди на свій сервер і встанови certbot. На офіційному сайті є генератор інструкцій, все що треба це обрати систему і софт
В інструкції, команда для отримання сертифікату буде подібною
Треба буде обрати домен під який робимо сертифікат і почекати декілька секунд. Після отримання сертифікату certbot встановить таймер для його автооновлення
📌 Не популярний домен треба брати через те, що в Let's Encrypt є обмеження на частоту оновлення сертифікату і це розповсюджується на субдомени
Перевірити оновлення сертифікату без його фактичного встановлення (чи все добре)
📟 Конфігурація nginx
Може виглядати наступним чином
📌 Замість YOUR_DOMAIN, SERVER_IP, AIOHTTP_PORT підставляй свої дані (якщо бот використовує Docker, то не забудь відкрити порти)
👉 На кінець, приклад використання aiohttp для вебхуку можна глянути тут
aiohttp, nginx, домен, Let's Encrypt #цікаве
• Що таке вебхук і які переваги він дає
• Пост про самопідписаний сертифікат
🔒 Let's Encrypt - це безкоштовний, автоматизований і відкритий центр сертифікації, створений некомерційною організацією Internet Security Research Group (ISRG)
🪩 FreeDNS - середовище, де інші програмісти можуть безкоштовно обмінюватися доменними іменами один з одним. З'явився у 2001 році, тому що його засновник, Джошуа Андерсон, хотів розважитися зі своїм хобі, пов'язаним з доменами
💠 aiohttp - асинхронний HTTP клієнт/сервер для asyncio та Python
• твій сервер визнається всіма (нап. при переході на його адресу в браузері не буде ніяких попереджень про безпеку)
• зручне автооновлення
• при встановленні вебхуку не треба надсилати файл
⬇️ Загалом:
• За допомогою FreeDNS отримуємо безкоштовний домен (для Let's Encrypt необхідно мати домен)
• Сертифікат для вебхуку беремо в Let's Encrypt
• certbot буде автоматично оновлювати наш сертифікат
• nginx буде отримувати POST запити на вебхук і пересилати їх на обробку до бота
• aiohttp буде приймати апдейти бота вже за допомогою Python
• Зареєструйся на сайті
• Перейди в розділ субдоменів і натисни «Add a subdomain»
• Переконайся, що в полі «Type» написано А
• У полі «Domain» обери бажаний домен (якщо натиснути «Many many more available» -> «Share Domain Registry», то з'явиться більше варіантів)
Краще обирати не дуже популярні, пізніше скажу чому. Продовжимо:
• в поле «Subdomain» введи назву субдомену (твоя авторська назва), виглядати буде як subdomain.domain
• в поле «Destination» введи IP адресу свого серверу
• після цього підтверди створення субдомену
📥 Отримання сертифікату
Далі зайди на свій сервер і встанови certbot. На офіційному сайті є генератор інструкцій, все що треба це обрати систему і софт
В інструкції, команда для отримання сертифікату буде подібною
sudo certbot certonly --nginx
Треба буде обрати домен під який робимо сертифікат і почекати декілька секунд. Після отримання сертифікату certbot встановить таймер для його автооновлення
Перевірити оновлення сертифікату без його фактичного встановлення (чи все добре)
sudo certbot renew --dry-run
📟 Конфігурація nginx
Може виглядати наступним чином
server {
listen 443 ssl;
server_name YOUR_DOMAIN;
ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem;
location / {
proxy_pass http://SERVER_IP:AIOHHTP_PORT;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍6💅3❤2🔥1
• Що таке апдейт
• Які є способи їх отримання
• Поняття реєстрації та обробника
• Ланцюг обробників
• Роутер і диспетчер - опис, порівняння
• Механізм Dependency Injection
• Детальний розбір мідлваря
• Фільтрація апдейтів
• Хендлер
⏫ Всі ці пункти я спробував розписати у максимально доступному форматі з поясненням різних моментів та описом елементарних помилок
https://botfather.dev/news/zhittyevij-cikl-apdejtu-v-aiogram
https://botfather.dev/news/lifecycle-of-an-update-in-aiogram
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥5❤2💅2
🦾 Local Bot API Server
Вихідний код Bot API Server (BAS) є відкритим - telegram-bot-api. Його можна запустити локально та надсилати запити на свій власний сервер замість https://api.telegram.org
🔥 Переваги бота, що працює з локальним BAS
• Завантаження файлів будь-якого розміру
• Надсилання файлів розміром до 2 ГБ
• Можна надсилати файли, використовуючи їхній локальний шлях і схему файлового URI
• Дозволяється використовувати HTTP URL для вебхуку
• Можна брати будь-які локальні IP та порти для вебхуку
• Ліміт max_webhook_connections розширюється до 100_000 (замість 100)
• Абсолютний локальний шлях отримується як значення поля file_path без необхідності завантажувати файл після запиту getFile
👉 Локальний BAS може покращити продуктивність ботів за рахунок більш низького пінгу та розширених лімітів
✏️ Загальні концепти
🦾 BAS це - додатковий рівнень абстракції для взаємодії з Telegram API у вигляді акаунта бота. Він є зручною реалізацією RestAPI для розробників і працює як фронт-енд для Telegram API. Зробити свій такий набагато складніше, BAS бере на себе інтерфейси Telegram API та роботу з MTProto. Всі боти взаємодіють з BAS і можуть працювати з ним на одному сервері
☑️ Global Bot API Server - звичайні боти взаємодіють з серверами Telegram через екземпляр telegram-bot-api, що обслуговується офіційно
Сам же користувач ніколи не взаємодіє з BAS напряму (тобто користувацький додаток Telegram ніколи не підключається безпосередньо до сервера, на якому запущений BAS). Всі комунікації проходять через сервери Telegram, які внутрішньо пересилають їх двом залученим клієнтам MTProto:
• додатку Telegram, що використовує користувач
• екземпляру BAS, що працює під капотом
☑️ telegram-bot-api точно такий самий сервер, з яким спілкуються боти на api.telegram.org. Тут немає ніяких додаткових або прибраних шарів. Боти працюватимуть набагато швидше, якщо переключаться на локальний BAS, розташований у відповідному регіоні. Покращити продуктивність також може робота з локальними файлами і більша пропускна здатність для вебхуків
🔌 Використання BAS
1. Генератор інструкцій для збірки серверу
2. Необхідно дістати api_id та api_hash свого аканту телеграм та вказати їх як аргументи (--api-id, --api-hash) чи змінні середовища (TELEGRAM_API_ID, TELEGRAM_API_HASH)
3. Щоб підключені до серверу боти мали розширені ліміти та інші функції, недоступні на https://api.telegram.org, обов'язково додай аргумент --local
4. BAS приймає лише HTTP-запити, тому для обробки HTTPS-запитів потрібно використовувати проксі з термінацією TLS
5. За замовчуванням BAS запускається на порту 8081, який можна змінити аргументом --http-port
📌 Примітки
📚 Using a local bot api server
📖 Bot api server general questions
Вихідний код Bot API Server (BAS) є відкритим - telegram-bot-api. Його можна запустити локально та надсилати запити на свій власний сервер замість https://api.telegram.org
• Завантаження файлів будь-якого розміру
• Надсилання файлів розміром до 2 ГБ
• Можна надсилати файли, використовуючи їхній локальний шлях і схему файлового URI
• Дозволяється використовувати HTTP URL для вебхуку
• Можна брати будь-які локальні IP та порти для вебхуку
• Ліміт max_webhook_connections розширюється до 100_000 (замість 100)
• Абсолютний локальний шлях отримується як значення поля file_path без необхідності завантажувати файл після запиту getFile
🦾 BAS це - додатковий рівнень абстракції для взаємодії з Telegram API у вигляді акаунта бота. Він є зручною реалізацією RestAPI для розробників і працює як фронт-енд для Telegram API. Зробити свій такий набагато складніше, BAS бере на себе інтерфейси Telegram API та роботу з MTProto. Всі боти взаємодіють з BAS і можуть працювати з ним на одному сервері
Сам же користувач ніколи не взаємодіє з BAS напряму (тобто користувацький додаток Telegram ніколи не підключається безпосередньо до сервера, на якому запущений BAS). Всі комунікації проходять через сервери Telegram, які внутрішньо пересилають їх двом залученим клієнтам MTProto:
• додатку Telegram, що використовує користувач
• екземпляру BAS, що працює під капотом
🔌 Використання BAS
1. Генератор інструкцій для збірки серверу
2. Необхідно дістати api_id та api_hash свого аканту телеграм та вказати їх як аргументи (--api-id, --api-hash) чи змінні середовища (TELEGRAM_API_ID, TELEGRAM_API_HASH)
3. Щоб підключені до серверу боти мали розширені ліміти та інші функції, недоступні на https://api.telegram.org, обов'язково додай аргумент --local
4. BAS приймає лише HTTP-запити, тому для обробки HTTPS-запитів потрібно використовувати проксі з термінацією TLS
5. За замовчуванням BAS запускається на порту 8081, який можна змінити аргументом --http-port
• Мігрувати бота на локальний сервер
• Перемістити бота з одного BAS на інший
• Всі файли зберігаються на серверах Telegram, а не на BAS. Але можливо, що BAS кешує файли (чи щось подібне)
• Є неофіційний Docker образ BAS від розробників aiogram
• На api.telegram.org заборонено використовувати адреси webhook тільки для IPv6. Очікується, що вони працюватимуть якщо BAS запущено в --local режимі
• getMe не слід використовувати для тестування продуктивності сервера. Сервер негайно відповідає на getMe, не викликаючи жодних інших мережевих запитів
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤3💅3🔥2
🛠 Інструменти
🔀 GitHub Actions - CI/CD платформа, що дозволяє автоматизувати твій build-test-deploy pipeline
Сама концепція CI/CD набагато ширша, але в рамках даного посту все спрощено: CI - це build контейнера, CD - його release і deploy
• GitHub Actions - в ролі вебхуку на push подію для репозиторію, який буде виконувати наші інструкції
• Docker - для побудови образу (але можна і без нього, наприклад запускати бота з systemd)
• Github Container Registry - для збереження образів (для приватної видимості, в DockerHub на це сильніші обмеження)
Щоб скористатись GitHub Actions пишуть workflow файли, а вони налаштовані для запуску однієї чи декількох задач
• YAML формат файлу
• Мають знаходитись в .github/workflows/
• Основна структура: події, змінні середовища та завдання
• І workflows і actions можна публікувати та перевикористовувати
Давай розберемо готовий приклад
name: Deploy to server
on:
push:
branches: [ "main" ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: <your-nick>/<image-name>:latest
permissions:
contents: read
packages: write
jobs:
publish:
name: Publish image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login
run: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and Push
run: |
docker build . -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
deploy:
needs: publish
name: Deploy app
runs-on: ubuntu-latest
steps:
- name: Setup ssh keys
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
- name: Connect-pull-up
run: |
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << EOF
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
cd ${{ secrets.WORK_DIR }}
docker compose pull
docker compose up -d
EOF
- name: Cleanup
run: rm -rf ~/.ssh
• Вказуємо події, при яких він буде працювати workflow (тут це push)
• Блок env, як не дивно, служить для вказання змінних середовища
• Блок permission встановлює права для GITHUB_TOKEN (також доступний через контекст github.token). Це спеціальний токен для автентифікації, що створюється для кожної job
• Блок jobs містить завдання, сприймай їх як етапи. Також вони можуть мати залежності, наприклад тут deploy чекає поки завершиться publish (needs). За замовчуванням завдання виконуються паралельно!
• checkout - офіційний action, його можна розуміти як git pull, щоб задача мала доступ до твого коду (використовує GITHUB_TOKEN)
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🔥2👀1💅1
Інлайн режим дозволяє робити багато речей, що будуть корисні в тій чи іншій ситуації, наприклад можна:
• Формувати повідомлення з кнопками
• Зробити каталог предметів
• Ділитися чимось від імені юзера
• Реалізувати пошуковий механізм
inline_query в дозволені, а далі в @BotFather > /mybots > твій бот > Bot Settings > Inline Mode і натисни Turn On⬇️ Розберемо всі нові типи і методи, які дає нам інлайн режим
• InlineQuery - апдейт, що створюється при використанні пошуку
• answerInlineQuery - метод, яким бот показує результати пошуку (ним відповідають на InlineQuery)
• InlineQueryResultsButton - об'єкт-кнопка, що знаходиться над результатами
• InlineQueryResult - загальний тип конкретного об'єкту в результаті
• InputMessageContent - загальний тип конкретного об'єкту для перевантаження надсилання
• ChosenInlineResult - апдейт, який доступний з увімкненим inline feedback в @BotFather для аналітики
• InlineQuery немає даних про тип чату
• answerInlineQuery може повертати результати з різними типами об'єктів (одночасно фото, аудіо..)
• InlineQueryResult для деяких підтипів має Cached форми, тобто результатом може бути фото, що вже збережене на серверах телеграм
• Якщо до повідомлення, яке надісло в інлайн режимі прикріплені кнопки, то при їх використанні бот отримає CallbackQuery без об'єкту Message
У нас є бот з інлайн режимом. У будь-якому чаті (де не заборонене медіа), як тільки ми вводимо в поле тексту його нікнейм, наприклад
@bot hello
то бот отримує апдейт InlineQuery з даними, в цьому випадку в запиті буде hello. Можна шукати і без слів, тоді запит буде порожнім рядком. Тепер ми змінимо запит на
@bot hello world
бот отримає новий апдейт з актуальним запитом hello world і поверне список результатів
Уявімо, що ти власник бота @gif. Він показує юзеру багато різних гіфок, але їх пошук займає деякий час. Під впливом великої кількості юзерів, бот може працювати не ефективно, бо на той же самий запит йому доведеться повертати одні й ті ж гіфки, хоча він вже відповідав на нього раніше. Для подібних ситуацій в методі answerInlineQuery є два параметра:
•
cache_time: int - час в секундах, скільки тримати результати запиту в кеші на серверах телеграм (за замовчуванням 5 хв)•
is_personal: bool - актуальний, якщо cache_time не дорівнює 0. Корисний, якщо ми хочемо кешувати результати індивідуально для юзера, а не для всіх одразуМетод answerInlineQuery показує максимум 50 об'єктів. Натомість, є можливість зробити підвантаження додаткових результатів як тільки юзер долистає до кінця поточних. Для цього є параметр
next_offset: str. Оскільки він має тип рядок, ми можемо передавати самі собі що завгодноНехай перший запит від юзера порожній, тож ми повертаємо всі результати що в нас є. Але їх більше ніж 50, тому ми ставимо next_offset="2". Якщо юзер долистає до кінця списку, боту прийде новий апдейт InlineQuery, де параметр offset="2". Таким чином ми знаємо, що треба підвантажити додаткові результати
За замовчуванням, при натисканні на результат він надсилається як тип, що його представляє. Наприклад InlineQueryResultPhoto природно надішле фото. Але це можна змінити параметром input_message_content
🍐 Книга Груші, про Inline Mode
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥4⚡1