import logging
from pymongo import MongoClient
from aiogram.contrib.middlewares.logging import LoggingMiddleware
from aiogram.dispatcher.filters import Text
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.types import ParseMode, CallbackQuery
from aiogram.utils import executor
from aiogram.types import (
ReplyKeyboardMarkup,
KeyboardButton,
InlineKeyboardButton,
InlineKeyboardMarkup,
)
from aiogram.utils.callback_data import CallbackData
# Сюда вводим токен от своего бота.
TOKEN = ""
# Сюда вводим id чата с админами/саппортами.
admin_chat_id = # Без кавычек
# В ковычки вставляем ссылку на подключение к БД, полученную выше
cluster = MongoClient(
"ссылка", ssl=True,ssl_cert_reqs='CERT_NONE'
)
# Подключаемся к БД
db = cluster["Guide1"]
# Подключаемся к таблице(коллекции)
workers = db["workers"]
# Создаём бота
bot = Bot(token=TOKEN)
# Присваиваем хранилище переменной storage, оно нам понадобится позже.
storage = MemoryStorage()
# Создаём диспетчер с аргументами bot и storage
dp = Dispatcher(bot, storage=storage)
# Добавляем встроенную миддлварь для удобного логгирования
logging.basicConfig(level=logging.INFO)
dp.middleware.setup(LoggingMiddleware())
# Пропишем простую клавиатуру Зарегистрироваться из одной кнопки
reg_button = KeyboardButton("Зарегистрироваться")
reg_keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
reg_keyboard.add(reg_button)
# Пропишем простую клавиатуру Отмена из одной кнопки
cancel_button = KeyboardButton("Отмена")
cancel_keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
cancel_keyboard.add(cancel_button)
# Пропишем инлайн клавиатуру для чата админов
# Задаём параметры Callback данных
reg_callback = CallbackData("reg", "status", "chat_id", "nick", "name", "age")
# Оборачиваем клаву в функцию, чтобы было удобнее использовать.
def inline(chat_id, nick, name, age):
confirm = InlineKeyboardButton(
text="invite",
callback_data=reg_callback.new(
status="1", chat_id=chat_id, nick=nick, name=name, age=age
),
)
cancel = InlineKeyboardButton(
text="kick",
callback_data=reg_callback.new(
status="0", chat_id=chat_id, nick="-", name="-", age="-"
),
)
conf_inline = InlineKeyboardMarkup()
conf_inline.insert(confirm).insert(cancel)
return conf_inline
# Создаём класс, передаём в него StatesGroup, он нам понадобится, чтобы провести юзера через анкету
# и сохранить каждый ответ. Если кто-то хочет почитать поподробнее, загуглите "aiogram машина состояний"
class Anketa(StatesGroup):
# внутри объявляем Стейты(состояния), далее мы будем вести пользователя по цепочке этих стейтов
name = State()
age = State()
# Начнём писать хэндлеры для сообщений, сначала пропишем хэндлер для команды /start
@dp.message_handler(commands=["start"])
async def welcome(message: types.Message):
# пропишем проверку юзера, есть ли он уже в нашей БД
# workers.find_one({"_id": message.chat.id}) как видите запрос выглядит очень просто, разжёвывать не буду
# на ютубе есть курс из 5 роликов по pymongo
if not workers.find_one({"_id": message.chat.id}):
await bot.send_message(
message.chat.id,
f"<b>Добро пожаловать, {message.from_user.username}!</b>\n\n"
f"Чтобы пройти регистрацию, нажмите на кнопку <b>Зарегистрироваться</b>",
parse_mode=ParseMode.HTML,
reply_markup=reg_keyboard,
)
else:
await bot.send_message(
message.chat.id, "Воркерам привет, остальным соболезную."
)
# Прописываем handler для кнопки отмены, мы заканчиваем State и возвращаем юзера в главное меню
# Продумывайте порядок написания handlerов так как они работают с верху в низ.
@dp.message_handler(Text(equals="Отмена"), state="*")
async def menu_button(message: types.Message, state: FSMContext):
await state.finish()
await bot.send_message(
message.chat.id, "Регистрация отменена.", reply_markup=reg_keyboard
)
# Теперь пропишем цепочку хэндлеров для анкеты
@dp.message_handler(Text(equals="Зарегистрироваться"), state="*")
async def name(message: types.Message, state: FSMContext):
await bot.send_message(
message.chat.id, "Введите своё имя:", reply_markup=cancel_keyboard
)
# Переходим на следующий стейт
await Anketa.name.set()
@dp.message_handler(state=Anketa.name, content_types=types.ContentTypes.TEXT)
async def age(message: types.Message, state: FSMContext):
# Записываем ответ в storage
await state.update_data(name=message.text)
await bot.send_message(
message.chat.id, "Введите возраст:", reply_markup=cancel_keyboard
)
await Anketa.age.set()
@dp.message_handler(state=Anketa.age, content_types=types.ContentTypes.TEXT)
async def confirmation(message: types.Message, state: FSMContext):
await state.update_data(age=message.text)
data = await state.get_data()
await bot.send_message(
message.chat.id, "Анкета успешно заполнена, после проверки вы получите ответ."
)
# Отправляем сообщение в чат админов с анкетой и добавляем 2 инлайн кнопки с callback данными
await bot.send_message(
admin_chat_id,
f"Поступила заявка от @{message.from_user.username}\n\n"
f'Имя: {data.get("name")}\n'
f'Возраст: {data.get("age")}',
reply_markup=inline(
f"{message.chat.id}",
f"{message.from_user.username}",
f'{data.get("name")}',
f"{data.get('age')}",
),
)
# Заканчиваем "опрос"
await state.finish()
# Теперь пропишем хэндлеры для inline
@dp.callback_query_handler(reg_callback.filter(status="0"))
# callback данные мы сразу же приобразуем в словарь для удобства работы
async def decline(call: CallbackQuery, callback_data: dict):
await call.answer()
print(call.message.message_id, "decl")
# Редачим сообщение в чате админов
await bot.edit_message_text(
"Заявка отклонена.", admin_chat_id, call.message.message_id
)
# Отправляем вердикт.
await bot.send_message(int(callback_data.get("chat_id")), "Увы и ах, иди ка ...")
@dp.callback_query_handler(reg_callback.filter(status="1"))
async def accept(call: CallbackQuery, callback_data: dict):
await call.answer()
print(call.message.message_id, "conf")
await bot.edit_message_text(
"Заявка одобрена.", admin_chat_id, call.message.message_id
)
# Отправляем вердикт.
await bot.send_message(
int(callback_data.get("chat_id")), "Добро пожаловать в команду."
)
# Добавляем работника в БД
workers.insert_one(
{
"_id": int(callback_data.get("chat_id")),
"name": callback_data.get("name"),
"age": callback_data.get("age"),
}
)
if __name__ == "__main__":
# Запускаем бота
executor.start_polling(dp, skip_updates=True)