Обучение Python+aiogram+MySQL
October 20, 2023

Обучение #5

Хочу сразу извиниться за то что задержал урок, была срочная работа, думаю такого больше не повториться🥺

Сегодня мы научимся работать с API сайта 365sms.org (Новый домен 365sms.ru)

Давайте попробуем понять зачем нам это.

  1. Это для того что-бы мы в бота смогли пихать любые сервисы которые дают нам API для связи с ними (и не только в ботов)
  2. Для автоматизации процессов

Использовать будем такую библиотеку как requests

pip install requests

Переходим на сайт "https://365sms.ru/api365"

и читаем документацию...

Что нам это дало?

Есть несколько разделов в api документации
Давайте разберём каждый по порядку:

  1. Описание протокола API - это нам не особо интересно, разве что строка описывающая какие запросы нам нужны "Все запросы (только GET) должны идти на данный адрес"
  2. Запрос количества доступных номеров getNumbersStatus - описан примерный запрос к сайту для получения кол-во доступных номеров
    https://365sms.org/stubs/handler_api.php?api_key=APIKEY&action=getNumbersStatus&country=COUNTRY&operator=OPERATOR

    А так же предоставлены переменные которые нужно будет заменить:
    APIKEY - ключ обеспечивает доступ к оплаченным услугам
    COUNTRY - Страна номера
    OPERATOR - Оператор номера. Если параметр не задан, будет задействован случайный оператор

    Ну и ответ от сервиса после сделанного запроса в формате json (позже к нему перейдём)
    {"vk_0":890,"ok_0":192,"wa_0":146,"vi_0":199,"tg_0":101,"wb_0":103,"go_0":467,"av_0":177,"fb_0":132,"tw_0":479}
  3. Запрос баланса getBalance - Получаем текущий баланс аккаунта на сайте
  4. Заказ номера getNumber - с помощью этого метода будем запрашивать номер
  5. Изменить статус setStatus - этот метод нам позволит менять статус номера
    Статусы:
    1. 3 - необходимо повторно смс
    2. 6 - активация успешно завершена
    3. 8 - отменить активацию
  6. Получить статус getStatus - с помощью данного метода мы получим статус номера (Статусы описаны выше)
  7. Запросить все цены getPrices - этот метод вернёт нам json объект со всеми ценами сервисов по ID страны

Давайте перейдём к методам запросов модуля requests

Метод POST:

Используется для отправки данных на сервер

Метод GET:

Используется для получения данных с сервера

Использовать будем метод GET

Создаём папку bot_requests_365

В папке создаём файлы:

  1. main.py - В нём будем писать весь код бота
  2. class_db.py - Скопируем в него модуль из предыдущих уроков
  3. test.py - В нём будут клавиатуры
  4. config.py - В него будем записывать все данные (токены, id админов и т.п.)

И так у нас получилась папка с содержанием:

Код для файла class_db.py:

import pymysql #Импортируем модуль pymysql который установили в первом уроке

class work_db:
	def __init__(self, host, port, user, password, database): #Функция для иницилизирования переменных в класс, запускать её не надо
		self.host = host
		self.port = port
		self.user = user
		self.password = password
		self.database = database

	def connect_db(self): #Функция для конекта к базе данных
		try:
			connection = pymysql.connect(
				host = self.host,
				port = self.port,
				user = self.user,
				password = self.password,
				database = self.database,
				cursorclass = pymysql.cursors.DictCursor
			)
			return connection #Если конект прошёл успешно, возвращаем переменную connection, для дальнейшей работы
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

	def connect_close(self, connection): #Передаём переменную connection если в коде подключились, после каждого подключения к базе данных и выполнения какого-либо когда, нужно закрывать подключение
		try:
			connection.close() #Закрываем подключение к базе данных
			return True #Если успешно закрыли подключение к базе, возвращаем True
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

	def create_table(self, connection, creat): #Функция для создания таблице в базе данных, в creat будем передавать текст запроса
		try:
			with connection.cursor() as cursor:
				cursor.execute(creat)
			connection.commit()
			return True
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

	def edit_table(self, connection, zapros, values):
		try:
			with connection.cursor() as cursor:
				cursor.execute(zapros, values)
			connection.commit()
			return True
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

	def info_table(self, connection, info, values): #Функция для получения данных из таблицы, в info будем передавать текст запроса
		try:
			with connection.cursor() as cursor:
				cursor.execute(info, values)
			return cursor.fetchall() #Возвращаем всё что нашли
		except Exception as eror:
			print(str(eror)+'\n\n') #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

	def insert_table(self, connection, zapros, values):
		try:
			with connection.cursor() as cursor:
				a = cursor.execute(zapros, values)
			connection.commit()
			return a
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

	def delete_from_table(self, connection, delete_, values): #Функция для редактирования данных в таблице, в delete_ будем передавать текст запроса
		try:
			with connection.cursor() as cursor:
				cursor.execute(delete_, values)
			connection.commit()
			return True
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

И так создадим ещё 1 файл для удобно работы с api сайта 365sms.ru

Название файла: module_sms.py

Напишем для этого файла следующий код:

import requests

class main_sms:
	def __init__(self, token):
		self.token = token #Токен будем передавать в класс
		self.link = 'https://365sms.ru/stubs/handler_api.php' #Ссылка для работы с api берём с сайта


	def get_balanse(self): #Функция для получения текущего баланса на сайте
		balans = requests.get(f'{self.link}?api_key={self.token}&action=getBalance') #Делаем get апрос для получения данных
		if 'ACCESS_BALANCE' in balans.text: #если мы успешно получили баланс то возращаем его
			spisok = balans.text.split(':') #Сплитуем(разделяем) текст по : который получили в ответе от сайта
			return spisok[1] #Возращаем баланс
		else:
			return balans.text #В ином случае возращаем весь текст который получили, для дальнейшей обработки


	def new_number(self, servis, strana, operator=None): #Функия для получения номера телефона
		if operator == None: #Если мы не передали оператор, то сайт нам выдаст рандомного оператора
			number = requests.get(f'{self.link}?api_key={self.token}&action=getNumber&service={servis}&country={strana}') #Делаем гет запрос без учёта оператора
		else: #В ином случае мы передаём в запрос ещё и оператора
			number = requests.get(f'{self.link}?api_key={self.token}&action=getNumber&service={servis}&operator={operator}&country={strana}')#Делаем гет запрос с учётом оператора
		return number.text #Возращаем всё что получили в ответ от сайта

	def new_status(self, idi, status): #Функция для изменения статуса номера
		stat = requests.get(f'{self.link}?api_key={self.token}&action=setStatus&status={status}&id={idi}') #Делаем гет запрос
		return stat.text #Возращаем всё что получили в ответ от сайта

	def get_status(self, idi): #Получаем статус номера по его id
		stat = requests.get(f'{self.link}?api_key={self.token}&action=getStatus&id={idi}') #Делаем гет запрос
		return stat.text #Возращаем всё что получили в ответ от сайта

	def get_all_price(self, strana, servis=None):
		if servis == None: #Если не передали сервис
			spisok = requests.get(f'{self.link}?api_key={self.token}&action=getPrices&country={strana}') #Делаем гет запрос без учёта сервиса
		else: #В ином случае при передаче сервиса
			spisok = requests.get(f'{self.link}?api_key={self.token}&action=getPrices&service={servis}&country={strana}') #Делаем гет запрос с учётом сервиса
		return spisok.json() #Возращаем всё что получили в ответ от сайта в формате json

Давайте перейдём к файлу test.py:

И напишем в него код с клавиатурой (для начала напишем 1 текстовую кнопку):

from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, KeyboardButton, ReplyKeyboardMarkup


mainy = [['Получить баланс']]
mark_menu = ReplyKeyboardMarkup(mainy, resize_keyboard=True)

Клавиатура создана, перейдём к файлу config.py:

token_bot = 'Токен вашего бота'
admin = Ваш id телеграма в формате 123
token_sms = 'Токен с сайта 365sms.ru/account'
#Данные для подключения к базе данных
host = 'localhost'
port = 8080
user = 'root'
password = 'password'
database = 'database_sms'

Токен бота берём в @BotFather

ID берём из @Bsc_Black_Secret_Club_bot -> Профиль

Токен смс берём с 365sms.ru/account

Берём из этого поля

Перейдём к самому интересному, к файлу main.py:

Импортируем нужные нам модули:

from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, KeyboardButton, ReplyKeyboardMarkup, BotCommand, WebAppInfo
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher import FSMContext
import config as c
import test as bb
import asyncio
import pymysql
from class_db import work_db
from module_sms import main_sms

Давайте напишем все экземпляры классов которые нам понадобятся:

bot = Bot(token=c.token_bot, parse_mode=types.ParseMode.HTML) #Берём токен бота из файла config.py который импортировали ранее в переменную c
dp = Dispatcher(bot, storage=MemoryStorage())
db_work = work_db(c.host, c.port, c.user, c.password, c.database) #Создаём экземпляр класса для работы с базой данных, передав все нужные параметры из конфига
work_sms = main_sms(c.token_sms) #Создаём экземпляр класса для работы с сервисом смс

Напишем обработчик всего текста, в который напишем обработку команды /start и текстовой кнопки "Получить баланс":

@dp.message_handler(content_types=['text'])
async def text(message: types.Message):
    id_user = message.from_user.id #Записываем id пользователя (Ранее писали message.chat.id)
    if message.text == '/start':
    	await bot.send_message(id_user, 'Хай, вы попали в смс бота для работы с сайтом 365sms.ru', reply_markup=bb.mark_menu) #При команде старт отправляем приветственное сообщение и рассказываем о себе, и выводим клавиатуру
    elif message.text == 'Получить баланс':
    	balanse_sms = work_sms.get_balanse() #Запрашиваем баланс с сайта по api
    	await bot.send_message(id_user, f'Текущий баланс: {balanse_sms}₽') #Пока выводим то что получили от сайта

У нас получился такой код:

from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, KeyboardButton, ReplyKeyboardMarkup, BotCommand, WebAppInfo
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher import FSMContext
import config as c
import test as bb
import asyncio
import pymysql
from class_db import work_db
from module_sms import main_sms



bot = Bot(token=c.token_bot, parse_mode=types.ParseMode.HTML) #Берём токен бота из файла config.py который импортировали ранее в переменную c
dp = Dispatcher(bot, storage=MemoryStorage())
db_work = work_db(c.host, c.port, c.user, c.password, c.database) #Создаём экземпляр класса для работы с базой данных, передав все нужные параметры из конфига
work_sms = main_sms(c.token_sms) #Создаём экземпляр класса для работы с сервисом смс



@dp.message_handler(content_types=['text'])
async def text(message: types.Message):
    id_user = message.from_user.id #Записываем id пользователя (Ранее писали message.chat.id)
    if message.text == '/start':
    	await bot.send_message(id_user, 'Хай, вы попали в смс бота для работы с сайтом 365sms.ru', reply_markup=bb.mark_menu) #При команде старт отправляем приветственное сообщение и рассказываем о себе, и выводим клавиатуру
    elif message.text == 'Получить баланс':
    	balanse_sms = work_sms.get_balanse() #Запрашиваем баланс с сайта по api
    	await bot.send_message(id_user, f'Текущий баланс: {balanse_sms}₽') #Пока выводим то что получили от сайта



if __name__ == "__main__":
    executor.start_polling(dp, skip_updates=True)

Запускаем и проверяем работоспособность:

Работает, отлично, теперь давайте сделаем обработчик ошибок:

Возможные ошибки у метода гет баланс:

И так у нас получился такой код:

elif message.text == 'Получить баланс':
    	balanse_sms = work_sms.get_balanse() #Запрашиваем баланс с сайта по api
    	try:
    		await bot.send_message(id_user, f'Текущий баланс: {float(balanse_sms):.2f}₽') #Пока выводим то что получили от сайта
    	except:
    		if 'BAD_KEY' in balanse_sms:
    			await bot.send_message(id_user, 'Не правильный токен для запроса, проверьте и обновите его!') #Если указали не верный токен
    		elif 'ERROR_SQL' in balanse_sms:
    			await bot.send_message(id_user, 'Ошибка на стороне сайта поопробуйте позже!') #Если на сайте произошла ошибка
    		else:
    			await bot.send_message(id_user, 'Ваш запрос был составлен не правильно!') #Если мы не правильно составили запрос (в уроке такого не будет)

Давайте впишем не правильный токен в конфиг -> токен смс

Запускаем и проверяем обработку ошибок:

Всё работает, мы молодцы!

Давайте добавим инлайн кнопку к сообщению с балансом (обновить)

Сделаем интересную систему и добавим время к сообщению:

Добавим импорты модулей для работы со временем:

from datetime import datetime
import pytz

Создадим функцию которая поможет нам получать время в Москве (в формате Часы:Минуты:Секунды):

def time_Moscow():
	# Устанавливаем часовой пояс Москвы
	moscow_tz = pytz.timezone('Europe/Moscow')

	# Получаем текущее время в часовом поясе Москвы
	moscow_time = datetime.now(moscow_tz)

	# Форматируем время в часы:минуты:секунды
	formatted_time = moscow_time.strftime('%H:%M:%S')
	return str(formatted_time) #Возвращаем строку с временем

Обновим обработчик кнопки получить баланс:

elif message.text == 'Получить баланс':
    	balanse_sms = work_sms.get_balanse() #Запрашиваем баланс с сайта по api
    	time_msk = time_Moscow() #Получаем время мск
    	try:
    		await bot.send_message(id_user, f'<b>[{time_msk}]</b> Текущий баланс: {float(balanse_sms):.2f}₽', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    		InlineKeyboardButton(text='Обновить', callback_data='new_balance'))) #Пока выводим то что получили от сайта
    	except:
    		if 'BAD_KEY' in balanse_sms:
    			await bot.send_message(id_user, 'Не правильный токен для запроса, проверьте и обновите его!') #Если указали не верный токен
    		elif 'ERROR_SQL' in balanse_sms:
    			await bot.send_message(id_user, 'Ошибка на стороне сайта поопробуйте позже!') #Если на сайте произошла ошибка
    		else:
    			await bot.send_message(id_user, 'Ваш запрос был составлен не правильно!') #Если мы не правильно составили запрос (в уроке такого не будет)

Добавлено:

  1. Получение времени в Москве
  2. Вывод времени и баланса c кнопкой обновить
await bot.send_message(id_user, f'<b>[{time_msk}]</b> Текущий баланс: {float(balanse_sms):.2f}₽', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    		InlineKeyboardButton(text='Обновить', callback_data='new_balance'))) #Пока выводим то что получили от сайта

Давайте напишем обработчик инлайн кнопок и напишем обработку нажатия кнопки обновить:

@dp.callback_query_handler(lambda call: True, state='*')
async def callback_inline(call, state: FSMContext):
    id_user = call.from_user.id
    if call.data == 'new_balance':
    	time_msk = time_Moscow() #Получаем время мск
    	balanse_sms = work_sms.get_balanse() #Запрашиваем баланс с сайта по api
    	await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=f'{call.message.html_text}\n<b>[{time_msk}]</b> Текущий баланс: {float(balanse_sms):.2f}₽', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    		InlineKeyboardButton(text='Обновить', callback_data='new_balance')))
  1. edit_message_text - с помощью этого метода у бота, мы будем редактировать сообщение

Давайте проверим как это работает:

Время работает, а что на счёт кнопки обновить?

Тоже работает, отлично!

Давайте добавим кнопку получить номер?

Конечно давайте, я вас не буду спрашивать а просто напишу)

Признайся, улыбнулся?)

В файл test.py меняем код на:

mainy = [['Получить баланс', 'Получить номер']]
mark_menu = ReplyKeyboardMarkup(mainy, resize_keyboard=True)

Напишем обработчик новой кнопки:

elif message.text == 'Получить номер':
    	#Будем получать номер для вк без оператора и регистрироваться через бота (Страну возьмём индонезию потому что дёшево ID: 6)
    	number = work_sms.new_number(servis='vk', strana=6)
    	#Обработаем ошибки:
    	if number == 'NO_NUMBERS': #Если пришёл ответ что нет номеров
    		await bot.send_message(id_user, 'Нет номеров с заданными параметрами, попробуйте позже, или поменяйте оператора, страну', reply_markup=bb.mark_menu)
    	elif number == 'NO_BALANCE': #Если на балансе не хватает денег на номер телефона
    		await bot.send_message(id_user, 'Закончились деньги на аккаунте', reply_markup=bb.mark_menu)
    	elif number == 'WRONG_SERVICE': #Если не правильно указали "servis"
    		await bot.send_message(id_user, 'Неверный идентификатор сервиса', reply_markup=bb.mark_menu)
    	else: #Если номер упешно получен
    		#В ответе будет такой формат: ACCESS_NUMBER:ID:NUMBER - Пример: ACCESS_NUMBER:234242:79123456789
    		#Делим ответ на список:
    		info_number = number.split(':')
    		#Итоговый список будет выглядеть так: ['ACCESS_NUMBER', 'ID', 'NUMBER'] - Пример: ['ACCESS_NUMBER', '234242', '79123456789']
    		#Индексы списка начинаються с 0
    		#т.е с индексом 0 - 'ACCESS_NUMBER', c индексом 1 - 'ID', c индексом 2 - 'NUMBER'
    		#Отправляем данные пользователю:
    		await bot.send_message(id_user, f'ID номера: {info_number[1]}\nНомер: <code>{info_number[2]}</code>', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    			InlineKeyboardButton(text='Изменить статус', callback_data=f'new_status_{info_number[1]}'))) #Передаём id номера в callback_data

Давайте добавим кнопки к последнему сообщению для работы с номером, например сделаем кнопку для обновления статуса:

await bot.send_message(f'ID номера: {info_number[1]}\nНомер: <code>{info_number[2]}</code>', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    			InlineKeyboardButton(text='Изменить статус', callback_data=f'new_status_{info_number[1]}'))) #Передаём id номера в callback_data

Давайте напишем обработчик нажатия этой кнопки:

elif 'new_status_' in call.data: #В call.data содержиться callback_data
    	id_number = int(call.data.replace('new_status_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
		#Грубыми словами с помощью replace мы заменяем new_status_ на ничего
		#Отправим клавиатуру с дальнейшими статусами, перед этим проверив текущий статус номера

Сейчас нам нужно проверить текущий статус номера:

elif 'new_status_' in call.data: #В call.data содержиться callback_data
    	id_number = int(call.data.replace('new_status_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
    	#Грубыми словами с помощью replace мы заменяем new_status_ на ничего
    	#Отправим клавиатуру с дальнейшими статусами, перед этим проверив текущий статус номера
    	status_number = work_sms.get_status(idi=id_number)
    	#Давайте в зависимости от статуса выведем клавиатуру:
    	if status_number == 'STATUS_WAIT_CODE': #Данный статус будет после получения номера он означает "Ожидание смс"
    		await call.answer('Текущий статус: Ожидает смс', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	elif status_number == 'STATUS_CANCEL': #Данный статус будет после отмены номера он означает "Активация отменена"
    		await call.answer('Текущий статус: Активация отменена', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	else: #Т.к. больше статусов которые может вернуть сайт нет, будем обабатывать успешное получение смс
    		#Делим полученное сообение на список с помощью split
    		kode_aktivate = status_number.split(':') #Получили список ["STATUS_OK", "CODE"]
    		await call.answer(f'Текущий статус: Смс получено\nКод: {kode_aktivate[1]}', show_alert=True)

Давайте обозначим каждый статус своей цифрой:

elif 'new_status_' in call.data: #В call.data содержиться callback_data
    	id_number = int(call.data.replace('new_status_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
    	#Грубыми словами с помощью replace мы заменяем new_status_ на ничего
    	#Отправим клавиатуру с дальнейшими статусами, перед этим проверив текущий статус номера
    	status_number = work_sms.get_status(idi=id_number)
    	#Давайте в зависимости от статуса выведем клавиатуру:
    	if status_number == 'STATUS_WAIT_CODE': #Данный статус будет после получения номера он означает "Ожидание смс"
    		int_status = 0 #Поставим 0 будет означать что номер только что получен
    		await call.answer('Текущий статус: Ожидает смс', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	elif status_number == 'STATUS_CANCEL': #Данный статус будет после отмены номера он означает "Активация отменена"
    		int_status = 1 #Поставим 1 будет означать что номер больше не активен (отменён)
    		await call.answer('Текущий статус: Активация отменена', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	else: #Т.к. больше статусов которые может вернуть сайт нет, будем обабатывать успешное получение смс
    		#Делим полученное сообение на список с помощью split
    		kode_aktivate = status_number.split(':') #Получили список ["STATUS_OK", "CODE"]
    		int_status = 2 #Поставим 2 будет означать что смс получен
    		await call.answer(f'Текущий статус: Смс получено\nКод: {kode_aktivate[1]}', show_alert=True)

Статусы обозначили, теперь давайте под каждый статус сделаем свою клавиатуру:

elif 'new_status_' in call.data: #В call.data содержиться callback_data
    	id_number = int(call.data.replace('new_status_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
    	#Грубыми словами с помощью replace мы заменяем new_status_ на ничего
    	#Отправим клавиатуру с дальнейшими статусами, перед этим проверив текущий статус номера
    	status_number = work_sms.get_status(idi=id_number)
    	#Давайте в зависимости от статуса выведем клавиатуру:
    	if status_number == 'STATUS_WAIT_CODE': #Данный статус будет после получения номера он означает "Ожидание смс"
    		int_status = 0 #Поставим 0 будет означать что номер только что получен
    		await call.answer('Текущий статус: Ожидает смс', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	elif status_number == 'STATUS_CANCEL': #Данный статус будет после отмены номера он означает "Активация отменена"
    		int_status = 1 #Поставим 1 будет означать что номер больше не активен (отменён)
    		await call.answer('Текущий статус: Активация отменена', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	else: #Т.к. больше статусов которые может вернуть сайт нет, будем обабатывать успешное получение смс
    		#Делим полученное сообение на список с помощью split
    		kode_aktivate = status_number.split(':') #Получили список ["STATUS_OK", "CODE"]
    		int_status = 2 #Поставим 2 будет означать что смс получен
    		await call.answer(f'Текущий статус: Смс получено\nКод: {kode_aktivate[1]}', show_alert=True)
    	#Сделаем стандартную клавиатуру с дефолтной нопкой:
    	klawa = InlineKeyboardMarkup(row_width=1).add(InlineKeyboardButton(text='Узнать статус', callback_data=f'new_status_{id_number}'))
    	if int_status == 0:
    		klawa.add(InlineKeyboardButton(text='Отменить активацию', callback_data=f'cancel_akt_{id_number}'))
    	elif int_status == 1:
    		klawa.add(InlineKeyboardButton(text='Получить новый номер', callback_data='new_number'))
    	else:
    		klawa.add(InlineKeyboardButton(text='Подтвердить SMS-код и завершить активацию', callback_data=f'true_aktiv_{id_number}'),
    			InlineKeyboardButton(text='Запросить еще одну смс', callback_data=f'dop_sms_{id_number}'))
    	await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=call.message.html_text, reply_markup=klawa)

Создали и отредактировали сообщение, теперь сделаем проверку try except, т.к. aiogram не позволяет редактировать сообщение на тот же самый текст что и был (что и с кнопками):

elif 'new_status_' in call.data: #В call.data содержиться callback_data
    	id_number = int(call.data.replace('new_status_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
    	#Грубыми словами с помощью replace мы заменяем new_status_ на ничего
    	#Отправим клавиатуру с дальнейшими статусами, перед этим проверив текущий статус номера
    	status_number = work_sms.get_status(idi=id_number)
    	#Давайте в зависимости от статуса выведем клавиатуру:
    	if status_number == 'STATUS_WAIT_CODE': #Данный статус будет после получения номера он означает "Ожидание смс"
    		int_status = 0 #Поставим 0 будет означать что номер только что получен
    		await call.answer('Текущий статус: Ожидает смс', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	elif status_number == 'STATUS_CANCEL': #Данный статус будет после отмены номера он означает "Активация отменена"
    		int_status = 1 #Поставим 1 будет означать что номер больше не активен (отменён)
    		await call.answer('Текущий статус: Активация отменена', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	else: #Т.к. больше статусов которые может вернуть сайт нет, будем обабатывать успешное получение смс
    		#Делим полученное сообение на список с помощью split
    		kode_aktivate = status_number.split(':') #Получили список ["STATUS_OK", "CODE"]
    		int_status = 2 #Поставим 2 будет означать что смс получен
    		await call.answer(f'Текущий статус: Смс получено\nКод: {kode_aktivate[1]}', show_alert=True)
    	#Сделаем стандартную клавиатуру с дефолтной нопкой:
    	klawa = InlineKeyboardMarkup(row_width=1).add(InlineKeyboardButton(text='Узнать статус', callback_data=f'new_status_{id_number}'))
    	if int_status == 0:
    		klawa.add(InlineKeyboardButton(text='Отменить активацию', callback_data=f'cancel_akt_{id_number}'))
    	elif int_status == 1:
    		klawa.add(InlineKeyboardButton(text='Получить новый номер', callback_data='new_number'))
    	else:
    		klawa.add(InlineKeyboardButton(text='Подтвердить SMS-код и завершить активацию', callback_data=f'true_aktiv_{id_number}'),
    			InlineKeyboardButton(text='Запросить еще одну смс', callback_data=f'dop_sms_{id_number}'))
    	try: #Если смогли отредактировать сообщение
    		await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=call.message.html_text, reply_markup=klawa)
    	except: #Если произошла любая ошибка
    		pass

Давайте проверим что мы с вами написали:

Всё работает, а что на счёт кнопки?
Давайте проверять:

Работает и даже кнопки поменялись)

Дальше проверим получение статуса путём нажатия на кнопку:

И она работает, давайте начнём регистрировать аккаунт в вк на этот номер

И так я отправил смс на номер, давайте проверим статус

Зарегать вк не получилось, смс на номер индонезии не приходит (отменил номер на сайте), решил поменять на рф озон, меняем код:

elif message.text == 'Получить номер':
    	#Будем получать номер для вк без оператора и регистрироваться через бота (Страну возьмём индонезию потому что дёшево ID: 6)
    	number = work_sms.new_number(servis='sg', strana=0)

У рф код страны 0
У озона код sg

Получаем новый номер через бота:

Нажимаем на кнопку изменить статус

Далее отправляем смс с озона:

Ждём, нажимаем на кнопку получить смс код

Смс опять не приходит...

Решил попробовать Qiwi, и наконец то получилось, ну ладно не суть

number = work_sms.new_number(servis='qw', strana=0)

На сайте есть код из смс:

Проверим статус через бота:

Работает, и опять же кнопки поменялись

Давайте напишем обработчик кнопки "Запросить ещё одну смс"

elif 'dop_sms_' in call.data:
    	id_number = call.data.replace('dop_sms_', '')
    	await call.answer('Новый статус установлен', show_alert=True)
    	work_sms.new_status(idi=id_number, status=3) #Устанавливаем статус 3 - нужна повторная смс
    	klawa = InlineKeyboardMarkup(row_width=1).add(InlineKeyboardButton(text='Узнать статус', callback_data=f'new_status_{id_number}'),
    		InlineKeyboardButton(text='Подтвердить SMS-код и завершить активацию', callback_data=f'true_aktiv_{id_number}'))
    	await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=call.message.html_text, reply_markup=klawa)

Теперь проверим как она работает

Запросил код для отключения смс оповещений:

Ждём...
Пришёл

Проверяем в боте

Хорошо, работает)

Напишем обработчик кнопки "Подтвердить SMS-код и завершить активацию":

elif 'true_aktiv_' in call.data:
    	id_number = call.data.replace('true_aktiv_', '')
    	await call.answer('Новый статус установлен', show_alert=True)
    	work_sms.new_status(idi=id_number, status=6) #Устанавливаем статус 6 - Подтвердить SMS-код и завершить активацию
    	klawa = InlineKeyboardMarkup(row_width=1).add(InlineKeyboardButton(text='Получить новый номер', callback_data='new_number'))
    	await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=f'{call.message.html_text}\nАктивация завершена', reply_markup=klawa)

Проверяем в боте

Теперь напишем обработчик кнопки "Отменить активацию":

elif 'cancel_akt_' in call.data:
    	id_number = call.data.replace('cancel_akt_', '')
    	status_number = work_sms.new_status(idi=id_number, status=8) #Устанавливаем статус 8 - Отменить активацию
    	await call.answer('Активация отменена', show_alert=True)
    	await bot.send_message(id_user, 'Хай, вы попали в смс бота для работы с сайтом 365sms.ru', reply_markup=bb.mark_menu)

Проверить не могу, поэтому в дз будет проверить на сколько это работает, в лс пришлёте отчёт)

Давайте напишем ещё обработчик кнопки "Получить новый номер":

Кароч решил просто скопировать код из текстовой кнопки "Получить номер", а собственно почему нет?

elif call.data == 'new_number':
    	await bot.delete_message(id_user, call.message.message_id) #Удаляем сообщение
    	#Будем получать номер для вк без оператора и регистрироваться через бота (Страну возьмём индонезию потому что дёшево ID: 6)
    	number = work_sms.new_number(servis='qw', strana=0)
    	#Обработаем ошибки:
    	if number == 'NO_NUMBERS': #Если пришёл ответ что нет номеров
    		await bot.send_message(id_user, 'Нет номеров с заданными параметрами, попробуйте позже, или поменяйте оператора, страну', reply_markup=bb.mark_menu)
    	elif number == 'NO_BALANCE': #Если на балансе не хватает денег на номер телефона
    		await bot.send_message(id_user, 'Закончились деньги на аккаунте', reply_markup=bb.mark_menu)
    	elif number == 'WRONG_SERVICE': #Если не правильно указали "servis"
    		await bot.send_message(id_user, 'Неверный идентификатор сервиса', reply_markup=bb.mark_menu)
    	else: #Если номер упешно получен
    		#В ответе будет такой формат: ACCESS_NUMBER:ID:NUMBER - Пример: ACCESS_NUMBER:234242:79123456789
    		#Делим ответ на список:
    		info_number = number.split(':')
    		#Итоговый список будет выглядеть так: ['ACCESS_NUMBER', 'ID', 'NUMBER'] - Пример: ['ACCESS_NUMBER', '234242', '79123456789']
    		#Индексы списка начинаються с 0
    		#т.е с индексом 0 - 'ACCESS_NUMBER', c индексом 1 - 'ID', c индексом 2 - 'NUMBER'
    		#Отправляем данные пользователю:
    		await bot.send_message(id_user, f'ID номера: {info_number[1]}\nНомер: <code>{info_number[2]}</code>', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    			InlineKeyboardButton(text='Изменить статус', callback_data=f'new_status_{info_number[1]}'))) #Передаём id номера в callback_data

Основные методы я с вами разобрал, сможете ли вы написать текстовую кнопку которая будет красиво выводить данные из метода getPrices?

Итоговый код:

from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, KeyboardButton, ReplyKeyboardMarkup, BotCommand, WebAppInfo
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher import FSMContext
import config as c
import test as bb
import asyncio
import pymysql
from class_db import work_db
from module_sms import main_sms
from datetime import datetime
import pytz


bot = Bot(token=c.token_bot, parse_mode=types.ParseMode.HTML) #Берём токен бота из файла config.py который импортировали ранее в переменную c
dp = Dispatcher(bot, storage=MemoryStorage())
db_work = work_db(c.host, c.port, c.user, c.password, c.database) #Создаём экземпляр класса для работы с базой данных, передав все нужные параметры из конфига
work_sms = main_sms(c.token_sms) #Создаём экземпляр класса для работы с сервисом смс


def time_Moscow():
	# Устанавливаем часовой пояс Москвы
	moscow_tz = pytz.timezone('Europe/Moscow')

	# Получаем текущее время в часовом поясе Москвы
	moscow_time = datetime.now(moscow_tz)

	# Форматируем время в часы:минуты:секунды
	formatted_time = moscow_time.strftime('%H:%M:%S')
	return str(formatted_time) #Возвращаем строку с временем


@dp.message_handler(content_types=['text'])
async def text(message: types.Message):
    id_user = message.from_user.id #Записываем id пользователя (Ранее писали message.chat.id)
    if message.text == '/start':
    	await bot.send_message(id_user, 'Хай, вы попали в смс бота для работы с сайтом 365sms.ru', reply_markup=bb.mark_menu) #При команде старт отправляем приветственное сообщение и рассказываем о себе, и выводим клавиатуру
    elif message.text == 'Получить баланс':
    	balanse_sms = work_sms.get_balanse() #Запрашиваем баланс с сайта по api
    	time_msk = time_Moscow() #Получаем время мск
    	try:
    		await bot.send_message(id_user, f'<b>[{time_msk}]</b> Текущий баланс: {float(balanse_sms):.2f}₽', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    		InlineKeyboardButton(text='Обновить', callback_data='new_balance'))) #Пока выводим то что получили от сайта
    	except:
    		if 'BAD_KEY' in balanse_sms:
    			await bot.send_message(id_user, 'Не правильный токен для запроса, проверьте и обновите его!') #Если указали не верный токен
    		elif 'ERROR_SQL' in balanse_sms:
    			await bot.send_message(id_user, 'Ошибка на стороне сайта поопробуйте позже!') #Если на сайте произошла ошибка
    		else:
    			await bot.send_message(id_user, 'Ваш запрос был составлен не правильно!') #Если мы не правильно составили запрос (в уроке такого не будет)
    elif message.text == 'Получить номер':
    	#Будем получать номер для вк без оператора и регистрироваться через бота (Страну возьмём индонезию потому что дёшево ID: 6)
    	number = work_sms.new_number(servis='qw', strana=0)
    	#Обработаем ошибки:
    	if number == 'NO_NUMBERS': #Если пришёл ответ что нет номеров
    		await bot.send_message(id_user, 'Нет номеров с заданными параметрами, попробуйте позже, или поменяйте оператора, страну', reply_markup=bb.mark_menu)
    	elif number == 'NO_BALANCE': #Если на балансе не хватает денег на номер телефона
    		await bot.send_message(id_user, 'Закончились деньги на аккаунте', reply_markup=bb.mark_menu)
    	elif number == 'WRONG_SERVICE': #Если не правильно указали "servis"
    		await bot.send_message(id_user, 'Неверный идентификатор сервиса', reply_markup=bb.mark_menu)
    	else: #Если номер упешно получен
    		#В ответе будет такой формат: ACCESS_NUMBER:ID:NUMBER - Пример: ACCESS_NUMBER:234242:79123456789
    		#Делим ответ на список:
    		info_number = number.split(':')
    		#Итоговый список будет выглядеть так: ['ACCESS_NUMBER', 'ID', 'NUMBER'] - Пример: ['ACCESS_NUMBER', '234242', '79123456789']
    		#Индексы списка начинаються с 0
    		#т.е с индексом 0 - 'ACCESS_NUMBER', c индексом 1 - 'ID', c индексом 2 - 'NUMBER'
    		#Отправляем данные пользователю:
    		await bot.send_message(id_user, f'ID номера: {info_number[1]}\nНомер: <code>{info_number[2]}</code>', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    			InlineKeyboardButton(text='Изменить статус', callback_data=f'new_status_{info_number[1]}'))) #Передаём id номера в callback_data


@dp.callback_query_handler(lambda call: True, state='*')
async def callback_inline(call, state: FSMContext):
    id_user = call.from_user.id
    if call.data == 'new_balance':
    	time_msk = time_Moscow() #Получаем время мск
    	balanse_sms = work_sms.get_balanse() #Запрашиваем баланс с сайта по api
    	await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=f'{call.message.html_text}\n<b>[{time_msk}]</b> Текущий баланс: {float(balanse_sms):.2f}₽', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    		InlineKeyboardButton(text='Обновить', callback_data='new_balance')))
    elif 'new_status_' in call.data: #В call.data содержиться callback_data
    	id_number = int(call.data.replace('new_status_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
    	#Грубыми словами с помощью replace мы заменяем new_status_ на ничего
    	#Отправим клавиатуру с дальнейшими статусами, перед этим проверив текущий статус номера
    	status_number = work_sms.get_status(idi=id_number)
    	#Давайте в зависимости от статуса выведем клавиатуру:
    	if status_number == 'STATUS_WAIT_CODE': #Данный статус будет после получения номера он означает "Ожидание смс"
    		int_status = 0 #Поставим 0 будет означать что номер только что получен
    		await call.answer('Текущий статус: Ожидает смс', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	elif status_number == 'STATUS_CANCEL': #Данный статус будет после отмены номера он означает "Активация отменена"
    		int_status = 1 #Поставим 1 будет означать что номер больше не активен (отменён)
    		await call.answer('Текущий статус: Активация отменена', show_alert=True) #Выводим табличку с текущим статусом (show_alert позваляет вывести табличку с кнопко "ОК")
    	else: #Т.к. больше статусов которые может вернуть сайт нет, будем обабатывать успешное получение смс
    		#Делим полученное сообение на список с помощью split
    		kode_aktivate = status_number.split(':') #Получили список ["STATUS_OK", "CODE"]
    		int_status = 2 #Поставим 2 будет означать что смс получен
    		await call.answer(f'Текущий статус: Смс получено\nКод: {kode_aktivate[1]}', show_alert=True)
    	#Сделаем стандартную клавиатуру с дефолтной нопкой:
    	klawa = InlineKeyboardMarkup(row_width=1).add(InlineKeyboardButton(text='Узнать статус', callback_data=f'new_status_{id_number}'))
    	if int_status == 0:
    		klawa.add(InlineKeyboardButton(text='Отменить активацию', callback_data=f'cancel_akt_{id_number}'))
    	elif int_status == 1:
    		klawa.add(InlineKeyboardButton(text='Получить новый номер', callback_data='new_number'))
    	else:
    		klawa.add(InlineKeyboardButton(text='Подтвердить SMS-код и завершить активацию', callback_data=f'true_aktiv_{id_number}'),
    			InlineKeyboardButton(text='Запросить еще одну смс', callback_data=f'dop_sms_{id_number}'))
    	try: #Если смогли отредактировать сообщение
    		await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=call.message.html_text, reply_markup=klawa)
    	except: #Если произошла любая ошибка
    		pass
    elif 'dop_sms_' in call.data:
    	id_number = call.data.replace('dop_sms_', '')
    	await call.answer('Новый статус установлен', show_alert=True)
    	work_sms.new_status(idi=id_number, status=3) #Устанавливаем статус 3 - нужна повторная смс
    	klawa = InlineKeyboardMarkup(row_width=1).add(InlineKeyboardButton(text='Узнать статус', callback_data=f'new_status_{id_number}'),
    		InlineKeyboardButton(text='Подтвердить SMS-код и завершить активацию', callback_data=f'true_aktiv_{id_number}'))
    	await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=call.message.html_text, reply_markup=klawa)
    elif 'true_aktiv_' in call.data:
    	id_number = call.data.replace('true_aktiv_', '')
    	await call.answer('Новый статус установлен', show_alert=True)
    	work_sms.new_status(idi=id_number, status=6) #Устанавливаем статус 6 - Подтвердить SMS-код и завершить активацию
    	klawa = InlineKeyboardMarkup(row_width=1).add(InlineKeyboardButton(text='Получить новый номер', callback_data='new_number'))
    	await bot.edit_message_text(chat_id=id_user, message_id=call.message.message_id, text=f'{call.message.html_text}\nАктивация завершена', reply_markup=klawa)
    elif 'cancel_akt_' in call.data:
    	id_number = call.data.replace('cancel_akt_', '')
    	status_number = work_sms.new_status(idi=id_number, status=8) #Устанавливаем статус 8 - Отменить активацию
    	await call.answer('Активация отменена', show_alert=True)
    	await bot.send_message(id_user, 'Хай, вы попали в смс бота для работы с сайтом 365sms.ru', reply_markup=bb.mark_menu)
    elif call.data == 'new_number':
    	await bot.delete_message(id_user, call.message.message_id) #Удаляем сообщение
    	#Будем получать номер для вк без оператора и регистрироваться через бота (Страну возьмём индонезию потому что дёшево ID: 6)
    	number = work_sms.new_number(servis='qw', strana=0)
    	#Обработаем ошибки:
    	if number == 'NO_NUMBERS': #Если пришёл ответ что нет номеров
    		await bot.send_message(id_user, 'Нет номеров с заданными параметрами, попробуйте позже, или поменяйте оператора, страну', reply_markup=bb.mark_menu)
    	elif number == 'NO_BALANCE': #Если на балансе не хватает денег на номер телефона
    		await bot.send_message(id_user, 'Закончились деньги на аккаунте', reply_markup=bb.mark_menu)
    	elif number == 'WRONG_SERVICE': #Если не правильно указали "servis"
    		await bot.send_message(id_user, 'Неверный идентификатор сервиса', reply_markup=bb.mark_menu)
    	else: #Если номер упешно получен
    		#В ответе будет такой формат: ACCESS_NUMBER:ID:NUMBER - Пример: ACCESS_NUMBER:234242:79123456789
    		#Делим ответ на список:
    		info_number = number.split(':')
    		#Итоговый список будет выглядеть так: ['ACCESS_NUMBER', 'ID', 'NUMBER'] - Пример: ['ACCESS_NUMBER', '234242', '79123456789']
    		#Индексы списка начинаються с 0
    		#т.е с индексом 0 - 'ACCESS_NUMBER', c индексом 1 - 'ID', c индексом 2 - 'NUMBER'
    		#Отправляем данные пользователю:
    		await bot.send_message(id_user, f'ID номера: {info_number[1]}\nНомер: <code>{info_number[2]}</code>', reply_markup=InlineKeyboardMarkup(row_width=1).add(
    			InlineKeyboardButton(text='Изменить статус', callback_data=f'new_status_{info_number[1]}'))) #Передаём id номера в callback_data

if __name__ == "__main__":
    executor.start_polling(dp, skip_updates=True)

Вот и дз сформировалось:

  1. Проверить работу кода (если есть ошибки исправить)
  2. Написать текстовую кнопку которая будет красиво выводить данные метода getPrices
  3. Подключить базу данных
    1. Регистрировать пользователей
    2. Считать номера которые они взяли в аренду
    3. Считать номера которые успешно завершили
    4. Записывать номера в отдельную таблицу
      1. ID номера
      2. Сам номер
      3. Пользователь который взял этот номер

ДЗ выполнить в течении 4х дней, так же в канале проходит голосование, что будем изучать дальше!

Спасибо за внимание

С вами был @Xacker_Name_new

Поддержка: @Bsc_Black_Secret_Club_bot

Канал: @education_python_aiogram