- 20.06.2022
- 23 868
- 218
- 36
- Награды
- 10
- Пол
- Муж.
Репутация:
- Автор темы
- Администратор
- Модератор
- Команда форума
-
- #1
Постараюсь объяснить основные аспекты создания бота в Telegram на Python с поддержкой библиотеки Aiogram.
1. Создаём необходимые файлы
Создаём папку с названием нашего Бота, в ней три файла: main.py , keyboard.py , config.py
Открываем эти файлы в любом текстовом редакторе ( я использую Sublime, ибо удобно переключаться между файлами + дизайн ).
2. Импорт модулей и компонентов
Устанавливаем библиотеку с помощью pip в консоли
Код:
pip install -U aiogram
Импортируем необходимое
Код:
# -*- coding: utf8 -*-
################################################################################################################################
from aiogram import Bot, types
from aiogram.utils import executor
from aiogram.dispatcher import Dispatcher
from aiogram.types import ReplyKeyboardRemove, ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
import asyncio
#################################################################################################################################
######################################################################
from aiogram.dispatcher import FSMContext ## ТО, ЧЕГО ВЫ ЖДАЛИ - FSM
from aiogram.dispatcher.filters import Command ## ТО, ЧЕГО ВЫ ЖДАЛИ - FSM
from aiogram.contrib.fsm_storage.memory import MemoryStorage ## ТО, ЧЕГО ВЫ ЖДАЛИ - FSM
from aiogram.dispatcher.filters.state import StatesGroup, State ## ТО, ЧЕГО ВЫ ЖДАЛИ - FSM
######################################################################
######################
import config ## ИМПОРТИРУЕМ ДАННЫЕ ИЗ ФАЙЛОВ config.py
import keyboard ## ИМПОРТИРУЕМ ДАННЫЕ ИЗ ФАЙЛОВ keyboard.py
######################
import logging # ПРОСТО ВЫВОДИТ В КОНСОЛЬ ИНФОРМАЦИЮ, КОГДА БОТ ЗАПУСТИТСЯ
Подключаем токен нашего Бота
Код:
storage = MemoryStorage() # FOR FSM
bot = Bot(token=config.botkey, parse_mode=types.ParseMode.HTML)
dp = Dispatcher(bot, storage=storage)
logging.basicConfig(format=u'%(filename)s [LINE:%(lineno)d] #%(levelname)-8s [%(asctime)s] %(message)s',
level=logging.INFO,
)
Попутно с этим заходим в config.py и прописываем наш ключ с BotFather :
Код:
Попутно с этим заходим в config.py и прописываем наш ключ с BotFather :
3. Первая команда, альтернативная замена БД и обычные кнопки
Перед тем как начать с команды старт, надо добавить кнопки, которые будут появляться при вводе команды.
Открываем keyboard.py и пишем такие строки, в начале импортируя библиотеку.
Код:
from aiogram import Bot, types
from aiogram.types import ReplyKeyboardRemove, ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
######################################################
start = types.ReplyKeyboardMarkup(resize_keyboard=True) # СОЗДАЕМ ВООБЩЕ ОСНОВУ ДЛЯ КНОПОК
info = types.KeyboardButton("Информация") # ДОБАВЛЯЕМ КНОПКУ ИНФОРМАЦИИ
stats = types.KeyboardButton("Статистика") # ДОБАВЛЯЕМ КНОПКУ СТАТИСТИКИ
start.add(stats, info) #ДОБАВЛЯЕМ ИХ В БОТА
Вернулись в main.py
Мы создаём бота, который может собирать статистику и делать рассылку. Значит, для начала, надо научить его записывать id пользователя, который отправил команду /start.
Для этого создаём текстовик user.txt в папке с ботом.Чтобы не возиться с БД ( На подобии Sql3 ), я сделаю его альтернативу через обычный txt - файл.
Начнём с команды /start
Код:
@dp.message_handler(Command("start"), state=None)
async def welcome(message):
joinedFile = open("user.txt","r")
joinedUsers = set ()
for line in joinedFile:
joinedUsers.add(line.strip())
if not str(message.chat.id) in joinedUsers:
joinedFile = open("user.txt","a")
joinedFile.write(str(message.chat.id)+ "\n")
joinedUsers.add(message.chat.id)
await bot.send_message(message.chat.id, f"ПРИВЕТ, *{message.from_user.first_name},* БОТ РАБОТАЕТ", reply_markup=keyboard.start, parse_mode='Markdown')
Что мы сделали?
Чтобы бот работал без остановок после выполнения команд, прописываем в самом конце main.pyОткрыли файл для чтения. Поставили условие, если в файле нет пользователя с таким id, то записываем его. Если есть - не трогаем.
Ну и естественно присылаем ответ на команду пользователя.
Код:
##############################################################
if __name__ == '__main__':
print('Монстр пчелы запущен!') # ЧТОБЫ БОТ РАБОТАЛ ВСЕГДА с выводом в начале вашего любого текста
executor.start_polling(dp)
##############################################################
Запускаем бота, через файл main.py.
Можно нажать два раза на файл, либо же открыть cmd и прописать
cd путь к папке с ботом
python main.py
Если у вас так-же - Вы всё сделали правильно, можно идти к самому боту.
Отправляем команду /start
Всё работает. Смотрим в файлик user.txt. Наш ID записался.
Важно! Кнопки не нажимаются, потому что в них ничего не добавили. Если нажмете на кнопку - выдаст ошибку в консоли ( кнопка никуда не ведёт ), но бот будет работать.
4. Оживляем обычные кнопки
Попробуем оживить кнопку "Информация" ?
Простенькие строчки, чтобы бот ответил на кнопку.
Код:
@dp.message_handler(content_types=['text'])
async def get_message(message):
if message.text == "Информация":
await bot.send_message(message.chat.id, text = "Информация\nБот создан специально для моих любимых девочек и мальчиков с lzt ", parse_mode='Markdown')
5. Вывод статистики (Inline-кнопки, callback_data)
Отлично. Кнопку оживили, но запускать пока что не будем. Добавим просмотр статистики, при помощи Inline-кнопки, callback_data, да еще и админку прикрутим)
Заходим в keyboard.py и пишем следующее
Код:
stats = InlineKeyboardMarkup() # СОЗДАЁМ ОСНОВУ ДЛЯ ИНЛАЙН КНОПКИ
stats.add(InlineKeyboardButton(f'Да', callback_data = 'join')) # СОЗДАЁМ КНОПКУ И КАЛБЭК К НЕЙ
stats.add(InlineKeyboardButton(f'Нет', callback_data = 'cancle')) # СОЗДАЁМ КНОПКУ И КАЛБЭК К НЕЙ
Так же, как мы делали обычные кнопки, но оформлено по эстетики Inline-кнопки
Теперь заходим в config.py
Пишем наш ID для админки
Взять его можно с нашего файла user.txt
Код:
botkey = '1770592647:AAHrIpW5XW6jYKmB56Kg63r_2LcCK8gOKtg' # ТОКЕН С BOTFATHER
admin = 1212341234
Возвращаемся в main.py и дополняем код.
Оживляем кнопку статистика.
Код:
@dp.message_handler(content_types=['text'])
async def get_message(message):
if message.text == "Информация":
await bot.send_message(message.chat.id, text = "Информация\nБот создан специально для моих любимых девочек и мальчиков с lzt ", parse_mode='Markdown')
if message.text == "Статистика":
await bot.send_message(message.chat.id, text = "Хочешь просмотреть статистику бота?", reply_markup=keyboard.stats, parse_mode='Markdown')
Далее прописываем "Ловлю callback" и что должно при этом выполняться.
Сделаем фичу, чтобы при нажатии на "да" Бот присылал не новое сообщение, а редактировал старое (выглядит более эстетично), выводя при этом количество пользователей бота.
Нам надо посчитать все строчки ( ID ) с текстовика user.txt и вывести их количество.
Всё это выглядит вот так
Код:
@dp.callback_query_handler(text_contains='join') # МЫ ПРОПИСЫВАЛИ В КНОПКАХ КАЛЛБЭК "JOIN" ЗНАЧИТ И ТУТ МЫ ЛОВИМ "JOIN"
async def join(call: types.CallbackQuery):
if call.message.chat.id == config.admin:
d = sum(1 for line in open('user.txt'))
await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text=f'Вот статистика бота: *{d}* человек', parse_mode='Markdown')
else:
await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text = "У тебя нет админки\n Куда ты полез", parse_mode='Markdown')
@dp.callback_query_handler(text_contains='cancle') # МЫ ПРОПИСЫВАЛИ В КНОПКАХ КАЛЛБЭК "cancle" ЗНАЧИТ И ТУТ МЫ ЛОВИМ "cancle"
async def cancle(call: types.CallbackQuery):
await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text= "Ты вернулся В главное меню. Жми опять кнопки", parse_mode='Markdown')
Зачем f перед кавычками?
У нас есть значение d которое содержит в себе количество строк. f-строки вступают в дело, чтобы помочь с форматированием.
Грубо говоря без f нашу несчастную d посчитают за текст.
Пора запускать бота.
Жмем "Статистика"
Жмём "Да"
Эти же действия, но без админки
Нажали "Информация"
6. Рассылка пользователям бота (+ отправка фотографии)
Делаем рассылку пользователям. Решил сделать так, чтобы с текстом отправлялась еще и фотка.
Скидываем фотку в нашу папку с ботом. Переименовываем её во что-то красивое.
Код:
@dp.message_handler(commands=['rassilka'])
async def rassilka(message):
if message.chat.id == config.admin:
await bot.send_message(message.chat.id, f"*Рассылка началась \nБот оповестит когда рассылку закончит*", parse_mode='Markdown')
receive_users, block_users = 0, 0
joinedFile = open ("user.txt", "r")
jionedUsers = set ()
for line in joinedFile:
jionedUsers.add(line.strip())
joinedFile.close()
for user in jionedUsers:
try:
await bot.send_photo(user, open('lzt.jpg', 'rb'), message.text[message.text.find(' '):])
receive_users += 1
except:
block_users += 1
await asyncio.sleep(0.4)
await bot.send_message(message.chat.id, f"*Рассылка была завершена *\n"
f"получили сообщение: *{receive_users}*\n"
f"заблокировали бота: *{block_users}*", parse_mode='Markdown')
Важно! Вставляем этот код сразу же после хэндлера со стартом.
Проверяйте какого фотка типа. У меня это jpg, поэтому я и пишу в коде .jpg
Коротко про код :
Команду для рассылки поставили /rassilka
Сделали так, чтобы бот рассылал текст, который написан через пробел к команде
Вот так: /rassilka текст рассылки
Опять же, выполняться будет только если ты админ.
Читаем ID с текстовика и рассылаем по ним сообщения с фоткой.
asyncio.sleep(0.4) - это задержка перед отправкой сообщения новому пользователю ( Чтобы не получить ограничение со стороны Telegram )
Записываем сколько пользователей получили рассылку и сколько пользователей заблокировали бота.
Когда рассылка завершится, выводить статистику рассылки.
Запускаем бота и проверяем.
Я добавил несколько аккаунтов в бота. На некоторых его заблокировал. Смотрим на результат :
7. FSM
Сделаем такую штучку.
Для админов, при вводе команды /me, бот просит отправить ссылку на профиль и ждёт ответа.
Мы отправляем ему свой профиль. Он его записывает в БД ( Но т.к мы хотим сделать всё по простому, я опять возьму текстовик вместо БД )
Далее он просит указать нам текст, который так же записывает в нашу "БД" ( Дальше будет понятно какой текст )
Добавим кнопку СОЗДАТЕЛЬ. Нажав на неё бот нам отправит ссылку на профиль и текст, который мы указали через /me
Приступим.
Создаём наши "БД" два файла в папку с ботом: link.txt и в них будут храниться наши ответы.
Переходим в main.py Я поместил код для этого выше старта, ибо всё, что связанно с FMS и State лучше писать в начале, дабы посреди основного кода он не отвлекал)
Прописываем State-группу. Называем её meinfo.
Q1 и Q2 что это?
Объяснить сейчас я это не могу, но по ходу процесса всё станет ясно (прописываем их столько, сколько у нас будет вопросов. В данном случае 2 ( бот просит ссылку и текст )
Код:
class meinfo(StatesGroup):
Q1 = State()
Q2 = State()
Теперь пишем, когда же бот начнет слушать наши ответы.
Код:
class meinfo(StatesGroup):
Q1 = State()
Q2 = State()
@dp.message_handler(Command("me"), state=None) # Создаем команду /me для админа.
async def enter_meinfo(message: types.Message):
if message.chat.id == config.admin:
await message.answer("начинаем настройку.\n" # Бот спрашивает ссылку
"№1 Введите линк на ваш профиль")
await meinfo.Q1.set() # и начинает ждать наш ответ.
@dp.message_handler(state=meinfo.Q1) # Как только бот получит ответ, вот это выполнится
async def answer_q1(message: types.Message, state: FSMContext):
answer = message.text
await state.update_data(answer1=answer) # тут же он записывает наш ответ (наш линк)
await message.answer("Линк сохранён. \n"
"№2 Введите текст.")
await meinfo.Q2.set() # дальше ждёт пока мы введем текст
@dp.message_handler(state=meinfo.Q2) # Текст пришел а значит переходим к этому шагу
async def answer_q1(message: types.Message, state: FSMContext):
answer = message.text
await state.update_data(answer2=answer) # опять же он записывает второй ответ
await message.answer("Текст сохранён.")
data = await state.get_data() #
answer1 = data.get("answer1") # тут он сует ответы в переменную, чтобы сохранить их в "БД" и вывести в след. сообщении
answer2 = data.get("answer2") #
joinedFile = open("link.txt","w", encoding="utf-8") # Вносим в "БД" encoding="utf-8" НУЖЕН ДЛЯ ТОГО, ЧТОБЫ ЗАПИСЫВАЛИСЬ СМАЙЛИКИ
joinedFile.write(str(answer1))
joinedFile = open("text.txt","w", encoding="utf-8") # Вносим в "БД" encoding="utf-8" НУЖЕН ДЛЯ ТОГО, ЧТОБЫ ЗАПИСЫВАЛИСЬ СМАЙЛИКИ
joinedFile.write(str(answer2))
await message.answer(f'Ваша ссылка на профиль : {answer1}\nВаш текст:\n{answer2}') # Ну и выводим линк с текстом который бот записал
await state.finish()
Запускаем проверяем.
Вводим команду /me
Всё работает и сохранилось в нашу "БД" текстовики link.txt и text.txt
Теперь создаём кнопку "Разработчик"
В keyboard.py добавляем к основным кнопкам еще одну.
Я ее не записал через запятую, а опустил ниже, чтобы она была одна снизу двух других кнопок.
Код:
from aiogram import Bot, types
from aiogram.types import ReplyKeyboardRemove, ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
######################################################
start = types.ReplyKeyboardMarkup(resize_keyboard=True) # СОЗДАЕМ ВООБЩЕ ОСНОВУ ДЛЯ КНОПОК
info = types.KeyboardButton("Информация") # ДОБАВЛЯЕМ КНОПКУ ИНФОРМАЦИИ
stats = types.KeyboardButton("Статистика") # ДОБАВЛЯЕМ КНОПКУ СТАТИСТИКИ
razrab = types.KeyboardButton("Разработчик") # ДОБАВЛЯЕМ КНОПКУ РАЗРАБОТЧИК
start.add(stats, info) #ДОБАВЛЯЕМ ИХ В БОТА
start.add(razrab)
######################################################
######################################################
stats = InlineKeyboardMarkup() # СОЗДАЁМ ОСНОВУ ДЛЯ ИНЛАЙН КНОПКИ
stats.add(InlineKeyboardButton(f'Да', callback_data = 'join')) # СОЗДАЁМ КНОПКУ И КАЛБЭК К НЕЙ
stats.add(InlineKeyboardButton(f'Нет', callback_data = 'cancle')) # СОЗДАЁМ КНОПКУ И КАЛБЭК К НЕЙ
######################################################
В main.py, где @dp.message_handler(content_types=['text'])
Прописываем следующее
Код:
@dp.message_handler(content_types=['text'])
async def get_message(message):
if message.text == "Информация":
await bot.send_message(message.chat.id, text = "Информация\nБот создан специально для моих любимых девочек и мальчиков с lzt ", parse_mode='Markdown')
if message.text == "Статистика":
await bot.send_message(message.chat.id, text = "Хочешь просмотреть статистику бота?", reply_markup=keyboard.stats, parse_mode='Markdown')
if message.text == "Разработчик":
link1 = open('link.txt', encoding="utf-8") # Вытаскиваем с нашей "БД" инфу, помещаем в переменную и выводим её
link = link1.read()
text1 = open('text.txt', encoding="utf-8") # Вытаскиваем с нашей "БД" инфу, помещаем в переменную и выводим её
text = text1.read()
await bot.send_message(message.chat.id, text = f"Создатель: {link}\n{text}", parse_mode='Markdown')
Запускаем, проверяем. Прописываем /start заново, чтобы новая кнопка появилась.
Отлично. Всё работает. Если сменить опять данные через /me, то при нажатии на "Разработчик" будут уже другие ваши текст и ссылка.
Последние темы в этом разделе:
- [Alex Erofeev] [Stepik] JavaScript: самый быстрый курс (2024)
- [Михаил Русаков] Написание лайфхаков на Python (2021)
- Пишем DDOS скрипт на Python
- [Саид Магомедов] Python. Микросервисы. Backend на FastAPI [Stepik] (2024)
- Как написать чат-бота на python
- Пишем скрипт по парсингу ответов из gdz.ru
- [Udemy] Кибербезопасность: тестирование на проникновение с помощью Python 3 (2022)
- [Udemy] [ENG] Продвинутый ИИ - глубокое обучение с подкреплением в Python (2024)
- Создание бесплатного API для генерации изображений, используя перехваченные запросы Flux
- [Андрей Сумин] [Stepik] Java с нуля до Junior + Подготовка к собеседованию (2024)