add verification batton
This commit is contained in:
parent
7573bfc51a
commit
9c1ac6e0bb
90
README.md
90
README.md
@ -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
117
bot.py
@ -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
78
bot2.py
@ -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)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user