add verification batton

This commit is contained in:
vasilytray 2025-03-11 21:19:43 +07:00
parent 7573bfc51a
commit 9c1ac6e0bb
3 changed files with 282 additions and 5 deletions

View File

@ -428,3 +428,93 @@ async def somebody_added(message: Message):
await message.reply(f"Привет, {user.full_name}") await message.reply(f"Привет, {user.full_name}")
``` ```
## Кнопки
### Обычные кнопки
Это то, что выводится внизу экрана
#### Кнопки как шаблоны
Напишем хэндлер, который будет при нажатии на команду /start отправлять сообщение с двумя кнопками в bot2.py.
С точки зрения Bot API, клавиатура — это массив массивов кнопок, а если говорить проще, массив рядов.:
```py
@dp.message(Command("start"))
async def cmd_start(message: types.Message):
kb = [
[
types.KeyboardButton(text="С пюрешкой"),
types.KeyboardButton(text="Без пюрешки")
],
]
keyboard = types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите способ подачи"
)
await message.answer("Как подавать котлеты?", reply_markup=keyboard)
```
Осталось научить бота реагировать на нажатие таких кнопок. Как уже было сказано выше,
необходимо делать проверку на полное совпадение текста.
Сделаем это при помощи магического *фильтра F*
```py
@dp.message(F.text.lower() == "с пюрешкой")
async def with_puree(message: types.Message):
await message.reply("Отличный выбор!", reply_markup=types.ReplyKeyboardRemove()) #удалим клавиатуру после ответа
```
#### Keyboard Builder
Сборщик клавиатур для генерации кнопок.
Нам пригодятся следующие методы:
``add(<KeyboardButton>)`` — добавляет кнопку в память сборщика;
``adjust(int1, int2, int3...)`` — делает строки по int1, int2, int3... кнопок;
``as_markup()`` — возвращает готовый объект клавиатуры;
``button(<params>)`` — добавляет кнопку с заданными параметрами, тип кнопки (Reply или Inline) определяется автоматически.
Создадим пронумерованную клавиатуру размером 4×4:
```py
# новый импорт!
from aiogram.utils.keyboard import ReplyKeyboardBuilder
@dp.message(Command("reply_builder"))
async def reply_builder(message: types.Message):
builder = ReplyKeyboardBuilder()
for i in range(1, 17):
builder.add(types.KeyboardButton(text=str(i)))
builder.adjust(4)
await message.answer(
"Выберите число:",
reply_markup=builder.as_markup(resize_keyboard=True),
)
```
#### Специальные обычные кнопки¶
На момент написания этой главы в Telegram существует шесть специальных видов обычных кнопок, не являющихся обычными шаблонами сообщений. Они предназначены для:
отправки текущей геолокации;
отправки своего контакта с номером телефона;
создания опроса/викторины;
выбора и отправки боту данных пользователя с нужными критериями;
выбора и отправки боту данных (супер)группы или канала с нужными критериями;
запуска веб-приложения (WebApp).
Поговорим про них подробнее.
**Отправка текущей геолокации.** Здесь всё просто: где пользователь находится, те координаты и отправляет. Это будет статическое гео, а не Live Location, который обновляется автоматически. Разумеется, хитрые юзеры могут подменить своё местонахождение, иногда даже на уровне всей системы (Android).
**Отправка своего контакта с номером телефона.** При нажатии на кнопку (с предварительным подтверждением) пользователь отправляет свой контакт с номером телефона боту. Те же хитрые юзеры могут проигнорировать кнопку и отправить любой контакт, но в этом случае на них можно найти управу: достаточно проверить в хэндлере или в фильтре равенство message.``contact.user_id == message.from_user.id``.
**Создание опроса/викторины.** По нажатию на кнопку пользователю предлагается создать опрос или викторину, которые потом отправятся в текущий чат. Необходимо передать объект KeyboardButtonPollType, необязательный аргумент type служит для уточнения типа опроса (опрос или викторина).
**Выбор и отправка боту данных пользователя с нужными критериями.** Показывает окно выбора пользователя из списка чатов юзера, нажавшего на кнопку. Необходимо передать объект KeyboardButtonRequestUser, в котором надо указать сгенерированный любым способом айди запроса и критерии, например, "бот", "есть подписка Telegram Premium" и т.д. После выбора юзера бот получит сервисное сообщение с типом **UserShared**.
**Выбор и отправка боту чата с нужными критериями.** Показывает окно выбора пользователя из списка чатов юзера, нажавшего на кнопку. Необходимо передать объект KeyboardButtonRequestChat, в котором надо указать сгенерированный любым способом айди запроса и критерии, например, "группа или канал", "юзер — создатель чата" и т.д. После выбора юзера бот получит сервисное сообщение с типом ChatShared.
**Запуск веб-приложения (WebApp)**. При нажатии на кнопку открывает WebApp. Необходимо передать объект WebAppInfo. В этой книге веб-аппы пока рассматриваться не будут.

117
bot.py
View File

@ -10,11 +10,13 @@ from aiogram.enums import ParseMode
from aiogram.enums.dice_emoji import DiceEmoji from aiogram.enums.dice_emoji import DiceEmoji
from aiogram import F, html from aiogram import F, html
from aiogram.types import Message from aiogram.types import Message
from aiogram.filters import Command, CommandObject from aiogram.filters import Command, CommandObject, CommandStart
from aiogram.utils.formatting import Text, Bold, as_list, as_marked_section, as_key_value, HashTag from aiogram.utils.formatting import Text, Bold, as_list, as_marked_section, as_key_value, HashTag
from aiogram.types import FSInputFile, URLInputFile, BufferedInputFile from aiogram.types import FSInputFile, URLInputFile, BufferedInputFile
# новый импорт! # новый импорт!
from aiogram.utils.markdown import hide_link #для скрытой ссылки from aiogram.utils.markdown import hide_link #для скрытой ссылки
# новый импорт!
from aiogram.utils.keyboard import ReplyKeyboardBuilder # для создания кнопок
from config_reader import config from config_reader import config
@ -59,15 +61,16 @@ async def cmd_start(message: types.Message):
"Если ты мне отправишь гифку, я тебе ей же и отвечу", "Если ты мне отправишь гифку, я тебе ей же и отвечу",
"----------", "----------",
"/more - Еще больше возможностей!", "/more - Еще больше возможностей!",
"/vfy - Получить подтверждение Вашего номера телефона",
marker="", marker="",
), ),
as_marked_section( as_marked_section(
Bold("Failed:"), Bold("Failed:"),
"Не смогу назвать номер твоего телефона :( )", "Не смогу полететь на луну:( ",
marker="", marker="",
), ),
HashTag("#я"), HashTag("#ищу"),
# Text( # Text(
# "Номер телефона, ", # "Номер телефона, ",
# Bold(message.contact.phone_number) # Bold(message.contact.phone_number)
@ -93,6 +96,7 @@ async def cmd_more(message: types.Message):
"e-mail,", "e-mail,",
"Номер телефона,", "Номер телефона,",
"Я распознаю их и напишу что нашел", "Я распознаю их и напишу что нашел",
"/special_buttons - выведу спецкнопки с командами",
"/dice - Подкину для тебя кубик, загадай число ;)", "/dice - Подкину для тебя кубик, загадай число ;)",
"/settimer <time> <message> - через установленное время сообще Message ;)", "/settimer <time> <message> - через установленное время сообще Message ;)",
"/hidden_link - Подкину для тебя угарную фотку ;)", "/hidden_link - Подкину для тебя угарную фотку ;)",
@ -311,6 +315,113 @@ async def cmd_hidden_link(message: Message):
f"Груша:" f"Груша:"
) )
# Специальные обычные кнопки
@dp.message(Command("special_buttons"))
async def cmd_special_buttons(message: types.Message):
builder = ReplyKeyboardBuilder()
# метод row позволяет явным образом сформировать ряд
# из одной или нескольких кнопок. Например, первый ряд
# будет состоять из двух кнопок...
builder.row(
types.KeyboardButton(text="Запросить геолокацию", request_location=True),
types.KeyboardButton(
text="Подтвердить контакт",
request_contact=True
)
)
# ... второй из одной ...
builder.row(types.KeyboardButton(
text="Создать викторину",
request_poll=types.KeyboardButtonPollType(type="quiz"))
)
# ... а третий снова из двух
builder.row(
types.KeyboardButton(
text="Выбрать премиум пользователя",
request_user=types.KeyboardButtonRequestUser(
request_id=1,
user_is_premium=True
)
),
types.KeyboardButton(
text="Выбрать супергруппу с форумами",
request_chat=types.KeyboardButtonRequestChat(
request_id=2,
chat_is_channel=True,
chat_is_forum=False
)
)
)
# WebApp-ов пока нет, сорри :(
await message.answer(
"Выберите действие:",
reply_markup=builder.as_markup(resize_keyboard=True),
)
# Прием нажатий нижних двух кнопок
@dp.message(lambda message: message.contact is not None)
async def handle_contact(message: types.Message):
# Проверяем, что контакт принадлежит отправителю
if message.from_user.id == message.contact.user_id:
await message.answer(
f"Спасибо за контакт, {message.contact.first_name}!\n"
f"Номер телефона: {message.contact.phone_number}",
reply_markup=types.ReplyKeyboardRemove() # Убираем клавиатуру
)
else:
await message.answer("Это не ваш контакт!")
@dp.message(F.user_shared)
async def on_user_shared(message: types.Message):
await message.answer(
f"Request {message.user_shared.request_id}. "
f"User ID: {message.user_shared.user_id}"
)
@dp.message(F.chat_shared)
async def on_user_shared(message: types.Message):
await message.answer(
f"Request {message.chat_shared.request_id}. "
f"Chat ID: {message.chat_shared.chat_id}"
)
@dp.message(Command("vfy"))
@dp.message(CommandStart(
deep_link=True, magic=F.args == "vfy"
))
async def cmd_start_vfy(message: types.Message):
builder = ReplyKeyboardBuilder()
builder.row(
types.KeyboardButton(
text="Подтвердить контакт",
request_contact=True
)
)
# Отправляем сообщение с клавиатурой
await message.answer(
"Для подтверждения номера телефона нажмите кнопку ниже:",
reply_markup=builder.as_markup(
resize_keyboard=True, # Опционально: автоматический размер
one_time_keyboard=True, # Опционально: скрыть после нажатия
input_field_placeholder="Подтвердите номер телефона" # Подсказка в поле ввода
)
)
# Хэндлер для обработки полученного контакта
@dp.message(lambda message: message.contact is not None)
async def handle_contact(message: types.Message):
# Проверяем, что контакт принадлежит отправителю
if message.from_user.id == message.contact.user_id:
await message.answer(
f"Спасибо за контакт, {message.contact.first_name}!\n"
f"Номер телефона: {message.contact.phone_number}",
reply_markup=types.ReplyKeyboardRemove() # Убираем клавиатуру
)
else:
await message.answer("Это не ваш контакт!")
# пока не работает, надо понять как получить file_id # пока не работает, надо понять как получить file_id
@dp.message(Command("gif")) @dp.message(Command("gif"))
async def send_gif(message: Message): async def send_gif(message: Message):

78
bot2.py
View File

@ -8,6 +8,9 @@ from aiogram.filters import Command, CommandObject, CommandStart
from aiogram import Bot, Dispatcher, types from aiogram import Bot, Dispatcher, types
from aiogram.client.default import DefaultBotProperties from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode from aiogram.enums import ParseMode
# новый импорт!
from aiogram.utils.keyboard import ReplyKeyboardBuilder
from config_reader import config from config_reader import config
@ -31,6 +34,45 @@ bot = Bot(
dp = Dispatcher() dp = Dispatcher()
dp["started_at"] = datetime.now().strftime("%Y-%m-%d %H:%M") dp["started_at"] = datetime.now().strftime("%Y-%m-%d %H:%M")
# @dp.message(Command("start"))
async def cmd_start(message: types.Message):
kb = [
[
types.KeyboardButton(text="С пюрешкой"),
types.KeyboardButton(text="Без пюрешки")
],
]
keyboard = types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите способ подачи"
)
await message.answer("Как подавать котлеты?", reply_markup=keyboard)
@dp.message(F.text.lower() == "с пюрешкой")
async def with_puree(message: types.Message):
await message.reply("Отличный выбор!", reply_markup=types.ReplyKeyboardRemove()) #удалим клавиатуру после ответа
@dp.message(F.text.lower() == "без пюрешки")
async def without_puree(message: types.Message):
await message.reply("Так невкусно!")
@dp.message(Command("reply_builder"))
async def reply_builder(message: types.Message):
builder = ReplyKeyboardBuilder()
for i in range(1, 17):
builder.add(types.KeyboardButton(text=str(i)))
builder.adjust(4)
await message.answer(
"Выберите число:",
reply_markup=builder.as_markup(resize_keyboard=True),
)
@dp.message(F.text.lower() == "10")
async def with_puree(message: types.Message):
await message.reply("Отличный выбор!", reply_markup=types.ReplyKeyboardRemove()) #удалим клавиатуру после ответа
@dp.message(Command("help")) @dp.message(Command("help"))
@dp.message(CommandStart( @dp.message(CommandStart(
deep_link=True, magic=F.args == "help" deep_link=True, magic=F.args == "help"
@ -50,9 +92,43 @@ async def cmd_start_book(
book_number = command.args.split("_")[1] book_number = command.args.split("_")[1]
await message.answer(f"Sending book №{book_number}") await message.answer(f"Sending book №{book_number}")
@dp.message(Command("vfy"))
@dp.message(CommandStart(
deep_link=True, magic=F.args == "vfy"
))
async def cmd_start_vfy(message: types.Message):
builder = ReplyKeyboardBuilder()
builder.row(
types.KeyboardButton(
text="Подтвердить контакт",
request_contact=True
)
)
# Отправляем сообщение с клавиатурой
await message.answer(
"Для подтверждения номера телефона нажмите кнопку ниже:",
reply_markup=builder.as_markup(
resize_keyboard=True, # Опционально: автоматический размер
one_time_keyboard=True # Опционально: скрыть после нажатия
)
)
# Хэндлер для обработки полученного контакта
@dp.message(lambda message: message.contact is not None)
async def handle_contact(message: types.Message):
# Проверяем, что контакт принадлежит отправителю
if message.from_user.id == message.contact.user_id:
await message.answer(
f"Спасибо за контакт, {message.contact.first_name}!\n"
f"Номер телефона: {message.contact.phone_number}",
reply_markup=types.ReplyKeyboardRemove() # Убираем клавиатуру
)
else:
await message.answer("Это не ваш контакт!")
# Запуск процесса поллинга новых апдейтов # Запуск процесса поллинга новых апдейтов
async def main(): async def main():
# Регистрируем хэндлер cmd_test2 по команде /start
# Запускаем бота # Запускаем бота
await dp.start_polling(bot) await dp.start_polling(bot)