about aiogram – Telegram
about aiogram
208 subscribers
2 photos
1 video
27 links
✏️ Про розробку телеграм ботів з використанням aiogram та інших бібліотек і технологій

🔍 Навігатор (inline mode) - @aiotipbot

🙃 Канал ведуть @Shaonis + @wakaree

#aiogram #bots #python
Download Telegram
🏭 Що таке callback factory (калбек фабрика)? #цікаве

👉 Callback Data Factory (CDF) - це інструмент для створення фільтрів на інлайн кнопках, він дозволяє поміщати в них данні (до 64 байтів - ліміт тг), які потім можна отримувати і відповідно реагувати (чимось схожий на enums)

🤨 І навіщо мені цим користуватись?
Корисна штука, коли вам в інлайн кнопках потрібна callback_data по типу item_1_73627352, item_2_7262626 (тобто декілька змінних у рядку). Для цього вже є готовий інструмент

👀 Як цим користуватись?

1️⃣ Імпортуємо CallbackData
from aiogram.filters.callback_data import CallbackData

2️⃣ Створюємо підклас CallbackData, наприклад
class ItemForSale(CallbackData, prefix='item'):
item_id: int
seller_id: int

📌 prefix - це обов'язковий кейворд, який робить такі фільтри унікальними (це типу їхнє ід чи ім'я, воно в callback_data на першому місці)

📌 атрибути item_id і seller_id - допомагають розрізняти змінні в callback_data

3️⃣ Робимо інлайн клавіатуру з кнопкою
from aiogram.utils.keyboard import InlineKeyboardBuilder

kb = InlineKeyboardBuilder()
kb.button(
text='Item',
callback_data=ItemForSale(
item_id=1,
seller_id=2,
).pack(),
)
kb = kb.as_markup()

📌 метод pack() повертає данні об'єкту ItemForSale у вигляді рядка "item:1:2", тому зміна порядку атрибутів може щось зламати (також є unpack, він робить навпаки - перетворює рядок в об'єкт)

📌 CDF використовують в поєднанні з InlineKeyboardBuilder, бо потрібна динамічна клавіатура (данні можуть змінюватися)

4️⃣ Ловимо данні по кліку на кнопку
@router.callback_query(ItemForSale.filter())
async def test(
call: CallbackQuery,
callback_data: ItemForSale,
):
print(callback_data.item_id)
print(callback_data.seller_id)

📌 тепер при натисканні на кнопку в консолі ми побачимо данні item_id і seller_id (які задавали при створенні кнопки)

📌 не можна змінювати назву аргументу callback_data в хендлері

📌 щоб ловити натискання ми користуємось методом filter() підкласу CallbackData, також в ньому можна писати правила магічного фильтру «F» якщо треба вказати щось точніше, наприклад F.item_id == 1 означає, що хендлер буде реагувати якщо заданий item_id в кнопці був 1

📚 Читати про це в документації
🍐 Книга Груші, про Callback factory
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
📼 Що таке FSM, ФСМ, машина станів? #цікаве

👉 Finite State Machine (FSM) - це абстрактна машина, яка може перебувати в одному з кінцевого числа станів у будь-який момент часу. FSM може переходити з одного стану в інший як того забажає розробник

🤨 І навіщо мені цим користуватись?

Це стане в нагоді коли ми захочемо приймати якісь дані від юзера, наприклад під час заповнення анкети, реєстрації на щось чи просто обслуговування клієнта

👀 Як цим користуватись?

1️⃣ Потрібно зберігати введені данні
from aiogram.fsm.storage.memory import MemoryStorage

dp = Dispatcher(storage=MemoryStorage())

📌 ми додали в диспетчер параметр storage, він визначає де бот буде зберігати данні, в цьому випадку є декілька варіантів, але для практики вистачить оперативної пам'яті (коли ми вимкнемо бота - всі данні пропадуть). Якщо ми не хочемо втрачати данні при зупинці бота, то можемо використати наприклад бд Redis.

📌 за замовчуванням storage=MemoryStorage(), я вказав його для більш зрозумілого пояснення

2️⃣ Будуємо машину станів
from aiogram.fsm.state import StatesGroup, State

class Application(StatesGroup):
name = State()
phone_number = State()
denoscription = State()

📌 один підклас StatesGroup - одна машина станів, у ній ми пишемо атрибути-екземпляри класу State, їх можна сприймати як флашки чи фільтри на які реагує хендлер

3️⃣ Вводимо нашого бота в стан
from aiogram.fsm.context import FSMContext

@router.message(Command('start'))
async def start_cmd(msg: Message, state: FSMContext):
await state.set_state(Application.name)
await msg.answer('Як тебе звати?')

📌 ми передаємо в хендлер аргумент state, за допомогою нього ми можемо керувати машинами станів, метод set_state задає новий стан, FSMContext потрібен для аннотації (так через IDE можна передивитись всі доступні методи)

📌 коли ми відправимо боту команду /start, то він увійде в стан і запитає наше ім'я

4️⃣ Ловимо повідомлення у стані
@router.message(Application.name)
async def get_name(msg: Message, state: FSMContext):
await state.update_data(name=msg.text)
await state.set_state(Application.name)
await msg.answer('Добре, тепер відправ свій номер')

📌 в якості фильтра хендлера ми вказуємо очікуваний стан - тобто назву класу + атрибут, метод update_data оновлює данні які ми зберігаємо в storage, важливо розуміти, що вони не залежать від станів

📌 далі по аналогії можна ловити іншу інформацію, головне не забувати міняти стан і данні в storage та вказувати правильний тип апдейту (атрибут декоратора хендлера)

📌 після того, як ми отримаємо збереженні данні у вигляді словника за допомогою методу state.get_data(), їх можна видалити методом state.clear() - проте він чистить і стан, якщо потрібно прибрати тільки стан, то state.set_state(state=None) або тільки видалити дані - state.set_data({})

📚 Читати про це в документації
🍐 Книга Груші, про FSM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🔌 Який хостинг обрати для бота в 2024р? #порада

💭 Я провів невелике опитування серед учасників моєї спільноти та склав рейтинг по згадуванню хостингів на спадання

🥇 AWS - 11
Американські веб сервіси Амазону. Є пробний період. Засновані у 2002р

📡 Регіони - для VPS Огайо, Північна Вірджинія, Орегон, Мумбаї, Сеул, Сінгапур, Сідней, Токіо, Центральна Канада, Франкфурт, Ірландія, Лондон, Париж Стокгольм
💎 Ціна VPS від $3.5

🥈 DigitalOcean - 8
Американська компанія зі штаб квартирою у Нью-Йорку. Заснована у 2011р. Має 15 датацентрів.

📡 Регіони - 9, є Амстердам
💎 Ціна VPS від $4

🥉 MVPS - 7
Компанія Республіки Кіпр. Заснована у 2018р. Має чудову безвідмовну роботу та гарну підтримку 24/7

📡 Регіони - 9 країн, є Нідерланди
💎 Ціна VPS від €4

4️⃣ HETZNER - 6
Німецька компанія. Заснована у 1997р. Hetzner Online один із крупних хостингів і центрів обробки данних у Європі. Має свої датацентри

📡 Регіони - Німеччина, Фінляндія, США
💎 Ціна VPS від €3.8

5️⃣ Oracle Cloud - 5
Американська компанія. Заснована у 2018р.

📡 Регіони - тут
💎 Ціни - тут

6️⃣ ZOMRO - 3
Нідерландська (Zomro B.V.) і Латвійська (Podaon SIA) компанія. Заснована у 2014 році в Україні. Має датацентр у Нідерландах

📡 Регіони - Нідерланди
💎 Ціна VPS від €4

7️⃣ VULTR - 3
Американська компанія. Заснована у 2014р

📡 Регіони - 32 штук (в датацентрах)
💎 Ціна VPS - від $2.5

8️⃣ CONTABO - 2
Німецька компанія. Заснована у 2003р
Має 10 датацентрів.


📡 Регіони - Німеччина, Об'єднане королівство, США, US, Сінгапур, Сідней
💎 Ціна VPS від €4.5

9️⃣ OVH - 1
Французька компанія. Заснована у 1999р. Найкрупніший європейський хостинг з 2011р. Має 34 датацентри у 8-ми країнах

📡 Регіони - датацентри, для VPS США
💎 Ціна VPS від $4.2

10) Cockbox - 1
Автоматизований анонімний сервіс. Вся оплата в криптовалюті. Ціль - забезпечити конфіденційність клієнтів

📡 Регіони - Молдова, Румунія
💎 Ціна VPS від $10

11) Google Cloud - 1
Платформа хмарних служб від компанії Google. Багато можливостей, є пробний період. Заснована в 2008р.

📡 Регіони - 35+, є Нідерланди
💎 Ціна VPS від $21

🎚 Всі ціни вказані за 1 місяць

🖥 VPS - приватний сервер який ділить ресурси з іншими юзерами на одному фізичному комп'ютері (на такому можна запустити бота)

⚡️ Особисто я користувався на практиці тільки MVPS і мені дуже подобається. Якщо хочете подякувати, ось реферальне посилання :D (моя винагорода тимчасова і у вас нічого не забирається)

🏅 Дякую всім, хто приймав участь в опитуванні!
Останнє оновлення 28.02.2024
6👍1🏆1🫡1
🗄 Що за бд редіс, Redis? #цікаве

👉 База даних Redis - (абр. remote dictionary server) швидке NoSQL сховище даних в форматі «ключ-значення», які зберігаються в оперативній пам'яті

📌 офіційної підтримки на Windows немає, але «якось» працювати можна

😀 І навіщо мені цим користуватись?
Наприклад, якщо взяти MemoryStorage, то данні які ми обробляємо за допомогою FSM будуть видалятися при зупинці бота. Але якщо ми хочемо, щоб данні зберігались? От для цього і підходить Redis (також з ним поєднують планувальники задач і черги, застосовують для роботи з хешем)

👀 Як цим користуватись?

1️⃣ Завантажуємо Redis

💭 Саме так і ніяк інакше, ви не під'єднаєтесь до того, чого у вас немає. Для кожної ОС є свої інструкції, які можуть різнитись, тому я не зможу описати процес встановлення персонально для вас - шукаємо це в інтернеті

2️⃣ Підключаємось до Redis
from aiogram.fsm.storage.redis import (
RedisStorage,
DefaultKeyBuilder,
)

storage = RedisStorage.from_url(
config.redis_url,
key_builder=DefaultKeyBuilder(
with_destiny=True,
),
state_ttl=timedelta(days=1),
data_ttl=timedelta(days=1),
)

dp = Dispatcher(storage=storage)

📌 клас RedisStorage потрібен для з'єднання з редісом, він приймає екземпляр коннекту до бд або url з даними для підключення. Тут використано метод from_url, саме він приймає url і просуває іменовані аргументи якби ми передали їх у RedisStorage напряму

📌 параметр key_builder можна використати для класу DefaultKeyBuilder, який потрібен для налаштування побудови ключів у редісі, ми вказуємо with_destiny=True, щоб вони були більш унікальними один для одного

📌 аргументи state_ttl і data_ttl - задають час в секундах, після закінчення якого ключи (для state і data відповідно) будуть видалятися. За замовчуванням у ключів в редісі немає TTL (time to live) - тому вони просто не видаляються і накопичуються

3️⃣ Користуємось FSM і поглиблюємо знання

💭 В принципі на цьому можна було б закінчити, оскільки після вищезазначених інструкцій нічого змінювати більше не треба, машини станів будуть працювати зовнішньо так само як це було б з MemoryStorage (дякуємо розробникам aiogram). Надалі можна лише поглиблюватись в те, як саме працює Redis і будуються (виглядають) ключі

📌 потренуватись можна в Docker

📌 ключі можуть бути - рядками, списками, множинами, хешами та сортованими множинами, а значення - рядками, числами, списками, множинами, хешами і бітовими рядками (в останньому до речі зберігаються ключі FSM)

📌 Redis може містити кілька баз даних, кожна з яких має унікальний номер. За замовчуванням їх 16 штук, від 0 до 15. Але це діло можна змінити в конфігураційних файлах

📚 Читати про це в документації
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
😀 Відправка в заданий час #цікаве

👉 Відправляти повідомлення через певний час - запланувати його, тобто зберігати до тих пір, поки воно не буде надіслано

😀 Що робити?
Якщо немає на стороні телеграм - треба робити у себе. Можна спробувати такі інструменти:
• asyncio
• apscheduler
• додаткові технології
│ RabbitMQ
│ Apache Kafka
│ Redis
╰ Celery

💭 Якщо контент просто треба надіслати пізніше, то можна взяти функції asyncio, наприклад create_task, call_later чи call_at. Проте, якщо вам потрібно більше гнучкості, наприклад видаляти задачу до її виконання чи відслідковувати прогрес розсилки, зупиняти її і так далі, то треба дивитись на інші варіанти

😀 В цьому пості ми розглянемо гнучкий і найбільш простий варіант для бота

APScheduler (абр. advanced python scheduler) - це бібліотека для планування завдань

В ній є 4 базові типи компонентів:
• тригери (date, interval, cron)
• сховища задач (in-memory, Redis..)
• планувальники (є AsyncIOScheduler)
• виконавці (відправляють завдання на виконання)

1️⃣ Екземпляр планувальника та його запуск

from apscheduler.schedulers.asyncio import AsyncIOScheduler

scheduler = AsyncIOScheduler()
scheduler.start()

📌 взагалі є декілька варіантів планувальника, але в нашому випадку потрібна асинхронність, тому беремо саме AsyncIOScheduler

2️⃣ Міні-інтерактив по документації

створення завдань
видалення завдань
призупинення і поновлення завдань
отримати всі завдання
змінити завдання
керування планувальником

💭 Можу запропонувати більш практичну статтю по APS від @Latand для aiogram 2.X - ось
Please open Telegram to view this post
VIEW IN TELEGRAM
2
😀 Про фільтри #цікаве

👉 Фільтр в aiogram - це об'єкт який викликається, приймає дані апдейту і порівнює їх з вашими умовами, він повертає boolean або dict

📌 на хендлер можна повісити декілька фільтрів через кому, тоді апдейт дійде до нього тільки якщо всі вони повернуть True (логічне AND). Якщо ми хочемо логічне OR, тоді можна створити ще один декоратор чи реєстрацію. З цього випливає те, що один хендлер може ловити декілька типів апдейтів

📌 Основні види фільтрів
• Текстові
• Магічні
Кастомні
CDF
Спец. апдейти
Для відлову помилок

✏️ Текстові
from aiogram.filters import Command

Command("start")
Command(commands=("foo", "bar"))

📌 фільтр Command створений для відлову команд, має для цього додаткові кейворди (prefix, ignore_mention), також з ним можна поєднувати CommandObject - цей об'єкт надає зручний спосіб вибирати будь-які дані команди, наприклад з "/get 65" взяти тільки 65. Для цього в хендлер передай кейворд command з анотацією із from aiogram.filters.command import CommandObject

🔮 Magic filers
from aiogram import F

🪄 Магічні фільтри працюють завдяки додатковій бібліотеці magic-filter, яку підтримують розробники aiogram, проте окремо вона має трохи менше функціоналу. F це аліас для MagicFilter

💭 «магічні вирази» можна розуміти і представляти як lambda функцію. Від мене: для розуміння, F у ролі фільтра можна уявляти як сам об'єкт апдейту, тобто Message, CallbackQuery і т.д.
Приклади (деякі методи універсальні):

🔻 Message
F.text == "hello"
F.photo
F.text.in_({"1", "2"})
F.text.contains("bar")
F.text.startswith("stop")
F.text.endswith("foo")
🔻
CallbackQuery
F.data == "my_data"
F.data.in_({"close", "cancel"})
F.chat.type == "private"
🔻
Regex
F.text.regexp(r'Hi, .+')

😀 Вони підтримують логічні оператори

• and через символ &
F.text == 1 & F.chat.id == 8
• or через символ |
F.text == "k" | F.text.len() == 1

• not через символ ~
~F.chat.id.in_({1, 2, 3})
ще є такі варіанти

📌 є прикольний метод as('value'), він передає «відфільтрований» апдейт у аргументи хендлера під вашою назвою value, приклад у Груші (листай нижче)

📌 MagicData із aiogram.filters.magic_data - це інструмент для створення фільтрів з вашими данними, які можуть бути передані в аргументи хендлера (через мідлвар, Distpacher чи іншим способом). Наприклад - MagicData(F.from_user.id == F.config.admin_id), де config переданий аргумент

💭 на те, що не вмістилося в пост я зробив посилання, див. вище «Основні види фільтрів»
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🪝 Як підняти вебхук #цікаве

👉 Webhook (для тг ботів) - це механізм отримання апдейтів шляхом ловлі POST-запитів до вашого веб-сервера, а не опитування телеграма на пряму

💡при polling (опитуванні) ми беремо апдейти з ендпоінту api.telegram.org, а от з вебхуком задаємо наше персональне посилання, куди вже сам телеграм буде відправляти апдейти, а ми їх ловити

🤨 І навіщо мені цим користуватись?

• у ситуації, коли бот має дуже велике навантаження, тобто отримує багато апдейтів за одиницю часу. Відомо, що опитування (getUpdates) дає можливість взяти максимально 100 апдейтів за виклик

• якщо не хочемо бачити записи в консолі про те, що не вдалося отримати апдейти. При polling кожний бот постійно робить запити на api.telegram.org навіть якщо апдейтів немає. Треба пам'ятати, що кожен виклик getUpdates має час виконання і певне навантаження

• цей механізм дає можливість розподіляти апдейти на потрібні вам сервера обробки і реалізувати горизонтальне масштабування проекту

👀 Як цим користуватись?

1️⃣ Домен або IP-адреса з сервером

💭 Очевидно, що телеграм буде слати апдейти на вказаний URL, до якого ви повинні мати доступ, тому необхідно завести свій сервер з необов'язковим доменом. Про організацію цього моменту можна почитати в інтернеті, а вибрати хостинг тут

2️⃣ Безпечне з'єднання з сервером

💭 Телеграм в якійсь мірі переймається про безпеку та конфіденційність своїх користувачів, тому не буде відправляти апдейти по небезпечному з'єднанню

• сервер повинен мати SSL/TLS сертифікат (другий наступник першого), щоб підтримувати HTTPS запити від телеграму. Дозволяється самописний сертифікат, але його встановлення відрізняється, для когось це може бути дещо складнішим (такий зазвичай прив'язують до IP-адреси)

📌 На каналі є пости про це
Самопідписаний сертифікат
Сертифікат від Let's Encrypt

3️⃣ Отримання апдейтів

💭 Є багато способів стежити за вхідними POST-запитами на ваш сервер, наприклад за допомогою:
• aiohttp
• FastAPI
• Flask
• reverse proxy server

🪄 Приклад з aiohttp (оригінал коду для мультибота):
bot.set_webhook(URL_FOR_UPDATES)

app = web.Application()
SimpleRequestHandler(
dispatcher=dp, bot=bot,
).register(
app, path=WEBHOOK_PATH,
)
setup_application(app, dp, bot=bot)
web.run_app(
app,
host=WEB_SERVER_HOST,
port=WEB_SERVER_PORT,
)

📌 bot.set_webhook - метод, який передає телеграму URL для подальшої відправки туди апдейтів

📌 web та app - модуль для взаємодії з HTTP протоколом і екземпляр-додаток aiohttp відповідно

📌 SimpleRequestHandler - спец. фільтр aiogram, який ловить лише POST запити і перенаправляє їх на обробку до нашого бота

📌 setup_application - функція aiogram, яка допомагає налаштувати startup-shutdown процеси aiohttp додатку разом з ботом

4️⃣ Безпека та секретні токени

💭 Якщо є сервер який приймає запити, то цим можуть скористатися хацкери. От наприклад, вони відправлять свій POST запит, бот подумає що це апдейт - неправильно сериліазує і виникне помилка. Щоб перешкоди подібному деякі перевіряють IP-адресу відправника (у тг вони статичні), хтось додає в запити телеграму свої ключі, які звіряють на сервері, але вибір лишається за вами, проте я не рекомендую користуватись API токеном бота в ролі секрету, якщо це не мультибот

5️⃣ Як безкоштовно спробувати вебхук без SSL сертифікату та домену (листай вниз посту)

🌀 Telegram Bot API - Webhooks
🍐 Якийсь вебхук Груші
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
@Tishka17 😳 GitHub #активіст

Розробник. Помічник в чаті. Контрибьютор aiogram

😀Чим займався/ється:

• веде свій особистий канал із дієвими порадами. Буде корисний будь-яким розробникам - @advice17

• творець aiogram-dialog. Це фреймворк з графічним інтерфейсом для тг ботів. Натхненний ідеями Android SDK та React.js (простіше кажучи він допомагає працювати з інлайн кнопками). Є бот для демонстрації роботи бібліотеки - @aiogram_dialog_demo_bot
~ 📚 Документація | 💭 Чат

https://news.1rj.ru/str/about_aiogram/17
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🔗 Deep Linking (Cheat sheet) #цікаве

👉 Telegram Deep Linking - (з англ. Глибокі посилання) це спеціальні види посилань, які певним чином обробляються клієнтами телеграм

🙂 Базові шаблони:
t.me/path?query
http://t.me/path?query
https://news.1rj.ru/str/path?query
tg://path?query

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

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

1️⃣ Згадування юзера по ід
👉 tg://user?id=<id>

📦 можна дістати з об'єкта User атрибутом url (from_user.url)

✏️ Цікаві факти про це посилання

2️⃣ Передача параметра у команді /start
👉 t.me/<username>?start=<parameter>

📦 from aiogram.utils.deep_linking import create_start_link

3️⃣ Додати бота в группу
👉 t.me/<username>?startgroup

📦 from aiogram.utils.deep_linking import create_startgroup_link

4️⃣ Додати бота в канал
👉 t.me/<username>?startchannel&admin=<permissions>

5️⃣ Надсилання підготовленого повідомлення
👉 tg://msg_url?text=<your_text>

6️⃣ Створення спойлеру
👉 <tg-spoiler>text</tg-spoiler>


👀 Цікавий канал посвячений Deep Linking

🌀 API > Deep Links
🍐 Книга Груші, повідомлення
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2👎1
🔒 Middleware, Мідлвар #цікаве

👉 Middleware (в aiogram) - це спеціальний класс (можна реалізувати у вигляді функції), який має контроль над апдейтом до того, як на нього відреагує бот

😀 І навіщо мені цим користуватись?

• Можна обробляти апдейт тільки якщо він відповідає нашим умовам
• Є можливість «блокувати апдейт» наче його і не було
• Загалом можна робити будь-які дії перед тим як бот відреагує на апдейт чи після цього

👀 Як цим користуватись?

• Екземпляр мідлваря треба реєструвати у диспетчера чи роутера з певним типом апдейту який він буде обробляти

📌 Є «зовнішні» та «внутрішні» реєстрації
Зовнішні - будуть завжди викликатись на вказаний тип апдейту
dp.<event>.outer_middleware()
Внутрішні - спрацюють якщо апдейт з потрібним типом потенційно потрапляє в якийсь хендлер (тобто пройде крізь всі фільтри)
<router>.<event>.middleware()

📌 Реєстрація мідлваря, який буде обробляти будь-які апдейти
dp.update.outer_middleware()

📌 Якщо комбінувати свій router і внутрішню реєстрацію, то мідлвар буде працювати тільки з хендлерами цього роутеру

🎒Пам'ятка новачка
1) Якщо мідлвар це класс, то в нього обов'язково повинен бути метод call - саме в ньому реалізується всі подальші дії після отримання апдейту
2) Точно знаємо який тип апдейтів приходить - анотуємо це.
3) За передачу апдейту по ланцюжку фільтрів і хендлерів відповідає виклик handler: await handler(event, data)
4) Виклик мідлваря приймає такі аргументи:

> handler - хендлер (відомий тільки якщо це внутрішній мідлвар)
> event - вхідна подія (підклас aiogram.types.base.TelegramObject)
> data - контекстні дані (словник), через них передаються аргументи у фільтри й хендлери

🪄 Приклади мідлварей

Лічильник (з доки) - обробляє апдейти типу Message які проходять фільтри і потрапляють в якийсь хендлер цього роутера. Передає в хендлер аргумент «counter»

Антифлуд (від Груші) - реєструється на тип Message, перевіряє чи є ід чату в тимчасовому кеші, якщо є, то блокує апдейт, інакше записує його в нього

Ловля альбомів - реєструється на Message і відловлює повідомлення з media_group_id, передає список елементів як аргумент «album» в хендлер

Передача сесій бд (від мене) - за допомогою async_sessionmaker (це з SQLAlchemy) дістає сесію для роботи з бд і дає її новому екземпляру класса з вашими запитами до бази данних, передає готовий клас як аргумент «db» в хендлер чи фільтр

Перевірка на бан (від мене) - дістає тг ід юзера і перевіряє його у списку заблокованих користувачів, блокує апдейт якщо результат пошуку True

📚 Читати про це в документації
🍐 Книга Груші, про Мідлвари
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👎1
📦 Збірка технологій для розробки тг ботів на Python #цікаве

😀 Фреймворки для TG Bot API
aiogramtelebot
python-telegram-bot

🌐 Веб фреймворки
aiohttpStarlette
FastAPISanic
QuartDjango

🖥 СУБД
SQLite3PostgreSQL
MySQL

🗃 NoSQL СУБД
RedisMongoDB

😀 ORM і міграції
SQLAlchemyAlembic

🛖 Робоче середовище
venvDocker

📅 Планувальники і брокери завдань
APShedulerRabbitMQ
Apache KafkaCelery
Nats

🧮 Тестування
unittestpytest
mockcoverage

🗜 DevOps і CI/CD
GitHub ActionsGitLab
Ansible

📌 Закреслені - не рекомендовані для використання у великих проектах. Акцент збірки зроблений на безоплатному використанні інструментів, можливості їх асинхронної роботи, популярності
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤖 Створення мультиботу #цікаве

👉 Мультибот - це бот, який може керувати іншими ботами. В aiogram 3.X такого можна написати з коробки (тобто для його створення вже є готові інструменти)

💭 Міньйон (мій сленг) - бот, що керується основним ботом

😀 І навіщо мені цим користуватись?

• запускати кастомні конфігурації ботів-шаблонів під свої потреби
• копіювати функціонал основного бота і використовувати його на різних токенах
• розділяти інтерфейс основного бота на декілька більш менших

👀 Як цим користуватись?

• для нормального мультибота необхідний вебхук (бонус внизу посту - як запустити найпростіший вебхук)

• окрім SimpleRequestHandler для основного бота, нам потрібен буде TokenBasedRequestHandler, щоб реєструвати інших ботів-посіпак, він буде брати їх токени для створення вебхук посилань, тому обовʼязково треба, щоб в path у методі register була константа {bot_token} (класи брати із aiogram.webhook.aiohttp_server)

🔅 MRE найпростішого мультибота: у нього міньйони додаються в рантаймі і підключаються до одного й того ж самого диспетчера (тобто виконують однакові дії)
https://github.com/aiogram/aiogram/blob/dev-3.x/examples/multibot.py

🪄 Псевдо-код для прикладу реєстрації міньйонів зі своїми роутерами (від мене)
https://pastebin.com/GSbGe34K

🎁 БОНУС - найпростіший вебхук в домашніх умовах

💭 Якщо в тебе вийшло запустити aiohttp додаток (web.Application) і ти хотів би потестувати бота, але при цьому немає SSL сертифікату або лінь його робити, то можна скористатись безкоштовним reverse proxy сервером який буде пересилати POST запити від телеграму до твого бота по захищеному тунелю (навіть якщо программа знаходиться за NAT-маршрутизатором чи брандмауером і в тебе сірий IP)

🛠 Називається цей інструмент - Ngrok

1️⃣ Завантажуємо його (на будь-яку ОС чи навіть docker)

2️⃣ Беремо порт на якому буде працювати наш aiohttp app, самого бота поки що не запускаємо. Далі заходимо в консоль і пишемо «ngrok http {your_port}» (для Windows там є окрема програма навіть). Далі ngrok видасть нам декілька посилань, беремо те, яке починається з https бо телеграм не буде слати на http

3️⃣ Змінюємо посилання (BASE_URL) у нашій програмі на те, яке взяли у ngrok. Запускаємо бота, готово! Тепер бот буде працювати на вебхуці

‼️ Це гарний спосіб, щоб тестувати бота на вебхуках, але не треба використовувати його в продакшині - краще зробити свій reverse proxy сервер, наприклад на nginx. До того ж кожний раз ngrok буде давати випадкову адресу, також там 2 години TTL у посилання (тобто прийдеться часто міняти), проте у зареєстрованих користувачів такого ліміту немає
Please open Telegram to view this post
VIEW IN TELEGRAM
3
😀 Привіт усім, вибачайте що постів довго не було. Справа в тому, що я вже зовсім скоро буду студентом 😀

😀 У нас на каналі з'явився новий редактор - @nullmatawasoradesu, він також має свій канал зі схожою тематикою @aiogram_hent, тому заходьте і туди, дізнавайтесь нове. Він час від часу буде писати пости, коли я буду зайнятий
Please open Telegram to view this post
VIEW IN TELEGRAM
1
😐 Якщо ти задаєш питання ось так.. #порада
«Доброго дня! Підкажіть, чому може виникати NazvaPomylky: opys pomylky
або взагалі ось так..
«Доброго дня! Допоможіть будь ласка, хто шарить у ...»
...то будь готовий, що шанси отримати допомогу будуть в рази менше.

🤔 А що не так?
Спочатку розглянемо другий кейс, коли ти взагалі не переходиш до проблеми одразу. Це мета-питання, яких потрібно уникати в подібних чатах!
Перший кейс, ти все таки надіслав текст помилки. Але ніхто не збирається витрачати свій час щоб відгадувати, що саме сталося. Назви й опису недостатньо, щоб зрозуміти твою проблему!
Те саме стосується й випадків, коли ти прислав тільки скріншот коду й просто питаєш "Чому це не працює?"

📋 Ось список порад, як корректно задати питання й отримати фідбек якомога швидше.
» Перед тим як написати у чат, спробуй розібратися з помилкою самостійно. Можливо, варто почитати документацію, або й зовсім підучити Python.
» Не задавай мета-питань.
» Якщо тобі погодилися допомогти у чаті, не потрібно одразу писати людині у ПП. (Більше про це »)
» Показуй повну помилку, від Traceback (most recent call last): до NazvaPomylky: opys pomylky.
» Не лінися ділитися контекстом помилки. Наприклад, якщо вона виникла у якомусь обробнику start_command, показуй увесь метод.
» Ділися кодом й трейсбеком на пасті, а не скидай полотно у чаті.
» Іноді, коли проблема неочевидна, просто показати код може бути недостатньо. Буде потрібен Мінімально відтворюваний приклад.
Please open Telegram to view this post
VIEW IN TELEGRAM
💬 Chat Actions #цікаве

Інколи може знадобитися виконати тривалу задачу, наприклад, надіслати файл великого розміру або дочекатися відповіді від API, щоб надіслати респонс користувачу. Щоб під час її виконання бот здавався більш "живим", а не просто наче підвисав у користувача — хорошою ідеєю буде надсилати "дію в чаті".

👀 Що ж це таке?

Нам відомо, що коли юзер щось друкує, надсилає фото тощо — ми бачимо відповідний статус під його іменем. Це дає зрозуміти, що співрозмовник не просто проігнорував твоє повідомлення, а збирається відповісти.
Так само і з ботом — щоб показати користувачу, що на його стороні щось відбувається, ми використовуєм аналогічний метод sendChatAction. Даний метод буде відображати вказаний розробником статус до п'яти секунд.

🤔 А якщо на виконання задачі мені потрібно більше, ніж п'ять секунд?

Це також передбачено фреймворком. Він забезпечує утиліту ChatActionSender.
Простий приклад:
@router.message(Command("generate_photo"))
async def generate_photo(message: Message, bot: Bot) -> Any:
async with ChatActionSender.upload_photo(bot=bot, chat_id=message.chat.id):
# Do something for a long time to get result
result = await generate_photo(...)
await message.answer_photo(photo=result)


⚒️ Є також спосіб забезпечити використовування chat action у всіх обробниках.


Спочатку потрібно зареєструвати ChatActionMiddleware (що таке мідлварі?) на подію message:
<router або dispatcher>.message.middleware(ChatActionMiddleware())

Тепер замість створення ChatActionSender у обробникуgenerate_photo ми можемо повісити мітку на обробник:
@router.message(Command("generate_photo"))
@flags.chat_action(ChatAction.UPLOAD_PHOTO)
async def generate_photo(message: Message) -> Any:
# Do something for a long time to get result
result = await generate_photo(...)
await message.answer_photo(photo=result)

За потребою можна змінювати й конфігурацію ChatActionSender, а не тільки тип дії, що надсилаємо:
@router.message(Command("generate_photo"))
@flags.chat_action(initial_sleep=2, action=ChatAction.UPLOAD_PHOTO, interval=3)
async def generate_photo(message: Message) -> Any:
# Do something for a long time to get result
result = await generate_photo(...)
await message.answer_photo(photo=result)


📚 Не полінися завітати в документацію!
Please open Telegram to view this post
VIEW IN TELEGRAM
😎42🤯21👍1
🤔 Новачки або aiogram roadmap #порада

В чаті можна часто можна помітити подібні питання:
• «Як створити бота? Хочу почати з цього»,
• «Чому треба писати равлики над функціями?»
• «Що таке polling?»

💭 З цього можна зробити висновок: aiogram дуже приваблює новачків, але він для них надто складний.

👉 Якщо ти хочеш навчитися створювати ботів, то не зважай на популярні відео в ютубі, в яких показують як написати бота. Автори цих відео роблять скоріше міні-обзори, у них немає мети серйозно тебе вчити, вони навіть можуть дати тобі погані приклади (якщо це не плейлист чи курс). Через це багато хто не знає з чого почати написання ботів і цю сферу часто асоціюють із чимось примітивним, хоча це не завжди так.

🎁 Пропоную новачкам, які хочуть з чогось почати, слідувати aiogram roadmap, яку я створив, щоб було комфортно поглиблюватись у фреймворк і загалом розробку ботів. Вона також буде корисна тим, хто має досвід. Сама roadmap обов'язково буде оновлюватись, чекаю ваші думки с приводу неї
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️⃣ Звідки і який менеджер бере локаль

Якщо у нас локалі знаходяться в БД, то треба створити менеджер самостійно як підклас
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

📌 Зверни увагу, що в set_locale перший позиційний аргумент це locale

5️⃣ Підключення та оформлення

• Імпортуємо і реєструємо I18nMiddleware
• В аргументах як ядро вказуємо FluentRuntimeCore (pip install fluent.runtime). Я обрав це, бо зараз доступні 3 ядра, чув що FluentCompileCore хочуть прибрати, а GNU gettext є і в build-in
• В аргументи мідлвара передаємо потрібний нам менеджер (чи самописний)
• Далі вказуємо директорії з перекладами. Їх приклади дивись в репозиторіях. Головне правильный шаблон шляху і розподілені .ftl файли
• Про синтаксис .ftl файлів читай тут
• Тепер використовуй у хендлерах i18n.get і якщо хочеш роби стаби

📌 LazyProxy - це фільтр, що сам дістає відповідний переклад і порівнює його із вхідним текстом

⭐️ aiogram_i18n
Please open Telegram to view this post
VIEW IN TELEGRAM
61👍1🔥1
👋 Всім привіт, в останні неділі писав бота. Це як мій пет проект, що буде корисний (на мій погляд) будь-якій активній людині в Telegram

Якщо коротко - можна зберігати повідомлення будь-якого типу, кастомізувати, і за допомогою інлайн режиму надсилати їх у будь-які чати. Є пошук, приватність, мови

👉 Ось сам бот @ntosbot

📃 Кому цікаво, які технології юзав:

• aiogram
• aiogram-dialog
• aiogram_i18n
• PostgreSQL
• psycopg3
• SQLAlchemy
• Alembic
• Redis
• Docker

🔌 Хостинг - https://www.mvps.net/?aff=30539 :D. Поки перша версія, треба тестити, буду радий почути фідбек

💭 Чи було б цікаво почитати про деплой ботів (Docker, SSH keys)?
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. Команда генерації може виглядати так:
ssh-keygen -t rsa -b 2048 -f ~/.ssh/key_name

-t алгоритм генерації ключа
-b довжина ключів в байтах
-f шлях, куди помістити ключі

📌 Чому назва одна, ключів же 2? Справа в тому, що публічний ключ буде мати назву key_name.pub, а от приватний просто key_name

2️⃣ Організація ключів

💭 А якщо у нас буде багато серверів, при тому декілька різних ключів для кожного користувача на сервері, все пхати в .ssh?

Для цього в директорії .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

📌 Ключі буде перенесено в домашню директорію user!

Вводимо пароль і ключі вже на сервері у потрібному місці!

📌 Після встановлення SSH ключів часто прибирають аутентифікацію паролем

👩‍💻 Docker Guide
🔑 SSH keys
Please open Telegram to view this post
VIEW IN TELEGRAM
3🤪1
✍️ Вебхук із самопідписаним сертифікатом + nginx, FastAPI #цікаве

Це компактна cheatsheet для встановлення бота на вебхук із самопідписаним сертифікатом на свій сервер з nginx та FastAPI

📌 Що таке вебхук і які переваги він дає

📚 Трохи теорії

📝 Самопідписані сертифікати SSL/TLS створюються, видаються та обслуговуються вручну, без центру сертифікації! Це означає, що тобі не треба буде комусь платити за оформлення та обслуговування сертифікату!

👩‍💻 nginx - це швидкий веб-сервер, який може бути зворотнім проксі, балансувальником навантаження і не тільки

👉 FastAPI - це сучасний, високопродуктивний веб-фреймворк для створення API за допомогою Python 3.8+ на основі стандартних підказок типів Python

• Самопідписаний сертифікат треба для безоплатного вебхуку
• 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 - куди покласти ключ і сертифікат

📌 при генерації обов'язково введи IP адресу серверу або домен в поле Common Name

👩‍💻 Робота з nginx

Для початку його треба встановити та запустити як сервіс. Приклад для Debian GNU/Linux
sudo apt-get update
sudo apt-get install nginx
sudo nginx -v
sudo systemctl enable nginx
sudo systemctl status nginx


Налаштуємо конфігурацію nginx
• робота із сертифікатом
• блокування запитів не від Telegram
• пересилання запитів до FastAPI

🫱 Вставляємо в /etc/nginx/nginx.conf (в блок http)
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;
}
}

📌 Заповни YOUR_IP - IP твого сервера, BOT_PORT - порт на якому працює бот (uvicorn наприклад)

📌 Після цього перезапуск
sudo systemctl restart nginx


🤝 Знайомимо бота з його вебхуком

‼️ Треба обов'язково запам'ятати, що Telegram вимагає пряме надсилання йому сертифікату як файлу, якщо він самопідписаний

👀 Приклад організаціїї FastAPI в боті. В main .py можна написати
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

‼️ треба бути в директорії з файлом | обов'язково додай @, тобто тут назва сертифікату це cert.pem | secret_token необов'язковий

😉 Корисні команди

Перевірити локально, чи працює сертифікат
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