
МИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ,
СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ
«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА» (СПбГУТ)
Кафедра информационных управляющих систем
Лабораторная работа № 06
по Б1.В.24 «ПРОГРАММИРОВАНИЕ КРИТИЧЕСКИХ СЕРВИСОВ»
Студент гр. ИСТ-
(подпись)
Проверил ___________________________ А. В. Параничев
(оценка и подпись)
Санкт-Петербург
2023 год
Программная реализация подключения локальной и протестированной удаленной базы данных в Telegram
ИНИЦИАЛИЗАЦИЯ TELEGRAM-БОТА
После перехода в Telegram и поиска @BotFather создан Telegram-бот «Подтверждение платежа» (https://t.me/Confirm_pay_bot), первоначальная информация о боте в формате JSON (по адресу
https://api.telegram.org/bot6182724250:AAFQ9uCP9WeaMBvbL-bukTtXXXTpWZiwNC0/getme, где botNNN:ABC - секретный токен бота).
{ "ok":true, "result": { "id":6182724250, "is_bot":true, "first_name":"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u043b\u0430\u0442\u0435\u0436\u0430", "username":"Confirm_pay_bot", "can_join_groups":true, "can_read_all_group_messages":false, "supports_inline_queries":false } }
Для бота выполнена инициализация с помощью команды /start (https://tlgrm.ru/docs/bots).
НАСТРОЙКА СРЕДЫ РАЗРАБОТКИ TELEGRAM-БОТА
Разработка Telegram-бота выполняется с помощью PyCharm на языке Python, после создания проекта устанавливается библиотека "чистого" API Telegram с помощью команды в консоли (https://docs-python.ru/packages/biblioteka-python-telegram-bot-python/ ):
pip install python-telegram-bot
Результат установки показан на рис. 1.
Рис. 1. Результат установки библиотеки python-telegram-bot.
ЗАПУСК TELEGRAM-БОТА ДЛЯ ВЫПОЛНЕНИЯ КОМАНД ПО ОТОБРАЖЕНИЮ ДАННЫХ
Далее создан новый файл config.py, в который вписываются токен и исходные данные:
API_TOKEN = "6182724250:AAFQ9uCP9WeaMBvbL-bukTtXXXTpWZiwNC0"
Далее добавлен прототип файла базы данных, включающий 4 списка по параметрам по теме "Сервис создания и отправки уникального пароля для подтверждения платежа на указанный телефонный номер": test_dbase.py :
import config import json class DBase:
def get_navigator_data(self, index_param): ret = "Данные не найдены!( Параметр:{}".format(index_param) index = int(index_param) if(index == 0): ret = "ФИО пользователей в JSON-формате \n" + json.dumps(self.T_NAV_NAMES, ensure_ascii = False) elif(index == 1): ret = "Номера телефонов пользователей в JSON-формате \n " + json.dumps(self.T_NAV_PRICES, ensure_ascii = False) return ret
T_NAV_NAMES = {'1': 'Петров Виктор Витальевич', '2': 'Иванов Виктор Петрович', '3': 'Анатольев Павел Евгеньевич', '4': 'Васильев Александр Петрович'} T_NAV_PRICES = {'1': '792384', '2': '736485', '3': '948532', '4': '894567'}
Содержимое файла main.py, в котором показано извлечение всех исходных данных по соответствующим командам
from telegram.ext import Updater, CommandHandler from telegram.ext import MessageHandler, Filters, InlineQueryHandler import test_dbase import config updater = Updater(token=config.API_TOKEN) dispatcher = updater.dispatcher dbase = test_dbase.DBase() def about(): return "Для извлечения исходных данных используйте команды:" \ "/navigators index, где index - номер параметра в таблице навигаторов;" \ "/vendors index, где index - номер параметра в таблице производителей" # функция обработки команды '/start' def func_start(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="Добро пожаловать в бота для подтверждения Ваших платежей! (локальная база данных)") # функция обработки текстовых сообщений def func_text(update, context): text_out = 'Получено сообщение: ' + update.message.text + '\n' + about() context.bot.send_message(chat_id=update.effective_chat.id, text=text_out) # функция обработки команды '/navs' def func_navigators(update, context): if context.args: text_reply = dbase.get_navigator_data(context.args[0]) context.bot.send_message(chat_id=update.effective_chat.id, text=text_reply) else: context.bot.send_message(chat_id=update.effective_chat.id, text='Необходимо указать аргумент команды /navs') # функция обработки команды '/vendors' def func_vendors(update, context): if context.args: text_reply = "Команда вызвана с первым параметром " + context.args[0] context.bot.send_message(chat_id=update.effective_chat.id, text=text_reply) else: context.bot.send_message(chat_id=update.effective_chat.id, text='Необходимо указать аргумент команды /vendors') # функция обработки не распознанных команд def func_unknown(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="Введена неизвестная команда!(") # обработчик команды '/start' start_handler = CommandHandler('start', func_start) dispatcher.add_handler(start_handler) # обработчик текстовых сообщений text_handler = MessageHandler(Filters.text & (~Filters.command), func_text) dispatcher.add_handler(text_handler) # обработчик команды '/navigators' navigators_handler = CommandHandler('navs', func_navigators) dispatcher.add_handler(navigators_handler) # запуск прослушивания сообщений updater.start_polling() # # обработчик нажатия Ctrl+C # updater.idle()
Рис. 2. Фрагмент работы кода с локальной БД в Telegram для бота "…"
В результате подключения к базе данных MySQL, сформирован следующий код для темы "Сервис создания и отправки уникального пароля для подтверждения платежа на указанный телефонный номер":
from telegram.ext import Updater, CommandHandler from telegram.ext import MessageHandler, Filters, InlineQueryHandler import config import connect_db import connect_dbase updater = Updater(token=config.API_TOKEN) dispatcher = updater.dispatcher activate = connect_dbase.dbase_mysql() db = activate.mysql_connect('db4free.net', 'sql7614974', 'qFFiyGEkk7', 'sql7614974') #db = DBase() lst_gnr_cd = None def about(): return "Для извлечения исходных данных используйте команды: \n" \ "/card index, где index - номер параметра в таблице Промокоды; \n" \ "/getcode Номер карты Желаемая скидка(%) - для получения промокода; \n " \ "/balance Номер карты, чтобы посмотреть количество баллов; \n" \ "/code для вывода промокодов; \n" # функция обработки команды '/start' def func_start(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="Добро пожаловать в бота для подтверждения Ваших платежей!") # функция обработки текстовых сообщений def func_text(update, context): text_out = 'Получено сообщение: ' + update.message.text + '\n' + about() context.bot.send_message(chat_id=update.effective_chat.id, text=text_out) import random import string def func_generate(update, context): if context.args: global lst_gnr_cd global phone_number phone_number = context.args[0] password_length = 6 # Длину пароля можно изменить password = generate_password(password_length) lst_gnr_cd = password context.bot.send_message(chat_id=update.effective_chat.id, text=f"Сгенерированный код для номера {phone_number}: {password}") val = (phone_number, password) activate = connect_dbase.dbase_mysql() db = activate.mysql_connect('db4free.net', 'sql7614974', 'qFFiyGEkk7', 'sql7614974') insrt_data = activate.insert_data(val, db) else: context.bot.send_message(chat_id=update.effective_chat.id, text="Укажите после команды /generate номер телефона. \nПример: \n/generate 1234567") def generate_password(length): characters = string.ascii_letters + string.digits # Все буквы и цифры password = ''.join(random.choice(characters) for _ in range(length)) return password def func_resend(update, context): if lst_gnr_cd is not None: context.bot.send_message(chat_id=update.effective_chat.id, text=f"Повторная отправка кода для номера {phone_number}: {lst_gnr_cd}") else: context.bot.send_message(chat_id=update.effective_chat.id, text="Пароль не был сгенерирован.") def select(update, context): if context.args: activate = connect_dbase.dbase_mysql() rows = activate.select_data(context.args[0]) context.bot.send_message(chat_id=update.effective_chat.id, text=rows) else: context.bot.send_message(chat_id=update.effective_chat.id, text="В аргументе нет номера выбираемой строки") def update(update, context): if len(context.args)==2: phone = context.args[0] new_password = context.args[1] activate = connect_dbase.dbase_mysql() if activate.update_data(phone, new_password): context.bot.send_message(chat_id=update.effective_chat.id, text="Для номера:\n"+ context.args[0] +"\nпароль был изменён") def delete(update, context): if context.args: phone = context.args[0] activate = connect_dbase.dbase_mysql() val = (phone) count_request = activate.delete_data(val) context.bot.send_message(chat_id=update.effective_chat.id, text="Пароль для номера:\n" + context.args[0] + "\nудален") # функция обработки не распознанных команд def func_unknown(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="Введена неизвестная команда!(") # обработчик команды '/start' start_handler = CommandHandler('start', func_start) dispatcher.add_handler(start_handler) # обработчик текстовых сообщений text_handler = MessageHandler(Filters.text & (~Filters.command), func_text) dispatcher.add_handler(text_handler) #генерация пароля командой '/generate' generate_handler = CommandHandler('generate', func_generate) dispatcher.add_handler(generate_handler) #повторная отправка пароля '/resend' resend_handler = CommandHandler('resend', func_resend) dispatcher.add_handler(resend_handler) select_handler = CommandHandler('select', select) dispatcher.add_handler(select_handler) update_handler = CommandHandler('update', update) dispatcher.add_handler(update_handler) delete_handler = CommandHandler('delete', delete) dispatcher.add_handler(delete_handler) # обработчик не распознанных команд unknown_handler = MessageHandler(Filters.command, func_unknown) dispatcher.add_handler(unknown_handler) # запуск прослушивания сообщений updater.start_polling() # обработчик нажатия Ctrl+C updater.idle() db.close()
Код дополнительного файла для подключения к базе данных connect_dbase.py
import time import json import pymysql class dbase_mysql: # -------------------------------- def __init__(self): self.db = self.mysql_connect('db4free.net','sql7614974','qFFiyGEkk7', 'sql7614974') if self.db is not None: print("Проверка чтения") #-------------------------------- def mysql_connect(self, Host, User, Password, Database): db = None try: db = pymysql.connect( host=Host, user=User, password=Password, database=Database) print("Подключение к БД успешно установлено. (время: {})".format(time.localtime())) except Exception as e: print("Подключение к БД НЕ установлено. \n{}\n{}\n(время :{})". e.__repr__(), e.args, format(time.localtime())) return 1 return db def insert_data(self, values, db): try: if db is not None: cur = db.cursor() sql = "INSERT INTO USER_PASS (USER_PHONE, PASS) VALUES (%s, %s)".format(values) print("Запрос {} успешно подготовлен".format(sql)) else: print("Не удалось подключиться к БД.") return 1 except: print("Запрос {} не удалось подготовить".format(sql)) return 2 try: # вставка значений (данных) в таблицу cur.execute(sql, values) # подтверждение транзакции (подтверждение ввода данных) db.commit() except: db.rollback() return 3 print(cur.rowcount, "Запись вставлена.") db.close() return 0 def select_data(self, index): db = self.mysql_connect('db4free.net','sql7614974','qFFiyGEkk7', 'sql7614974') ret = None try: if db is not None: cur = db.cursor() sql = "SELECT * FROM USER WHERE User_id = {}".format(index) print("Запрос {} успешно подготовлен.".format(sql)) else: print("Не удалось подключиться к БД.") return ret except: print("Запрос {} не удалось подготовить.".format(sql)) return ret try: cur.execute(sql) print("Выполнено: cur.execute(sql)") rows = cur.fetchall() print("Выполнено: rows = cur.fetchall()") row_headers = [x[0] for x in cur.description] print("Выполнено: row_headers = [x[0] for x in cur.description]") json_data = [] for row in rows: json_data.append(dict(zip(row_headers, row))) print("Выполнено: json_data.append(dict(zip(row_headers, row)))") print("json_data = \n{}".format(json_data)) ret = json.dumps(json_data, skipkeys=False, ensure_ascii=False, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, sort_keys=False) print("Успешно получен результат на запрос:\n{}".format(ret)) except Exception as e: print("Запрос {} не удалось выполнить на сервере. Ошибка:\n{}\n{}".format(sql, e.__repr__(), e.args)) finally: db.close() return ret def update_data_my(self, values, db): try: if db is not None: cur = db.cursor() sql = "UPDATE USER_PASS SET PASS = %s WHERE USER_PHONE = %s".format(values) print("Запрос {} успешно подготовлен".format(sql)) else: print("Не удалось подключиться к БД.") return 1 except: print("Запрос {} не удалось подготовить".format(sql)) return 2 try: # вставка значений (данных) в таблицу cur.execute(sql, values) # подтверждение транзакции (подтверждение ввода данных) db.commit() except: db.rollback() return 3 print(cur.rowcount, "Запись обновлена.") db.close() return 0 def update_data(self, phone, new_password): try: if self.db is not None: cur = self.db.cursor() sql = "UPDATE USER_PASS SET PASS = %s WHERE USER_PHONE = %s" cur.execute(sql, (new_password, phone)) self.db.commit() print("Вроде запрос выполнен") return True else: print("Не удалось подключиться к БД.") return False except Exception as e: print("Запрос не удалось выполнить на сервере. Ошибка:\n{}\n{}".format(e.__repr__(), e.args)) return False def delete_data(self, condition): db = self.mysql_connect('db4free.net', 'sql7614974', 'qFFiyGEkk7', 'sql7614974') try: if db is not None: cur = db.cursor() sql = "DELETE FROM USER_PASS WHERE USER_PHONE = {}".format(condition) print("Запрос {} успешно подготовлен.".format(sql)) cur.execute(sql) db.commit() print("Запись успешно удалена.") else: print("Не удалось подключиться к БД.") except Exception as e: print("Запрос {} не удалось выполнить на сервере. Ошибка:\n{}\n{}".format(sql, e.__repr__(), e.args)) db.rollback() finally: db.close() if __name__ == '__main__': activate = dbase_mysql() val = (364845) count_request = activate.delete_data(val)
Рис. 3. Фрагмент работы кода с удаленной БД в Telegram для бота "Сервис создания и отправки уникального пароля для подтверждения платежа на указанный телефонный номер": создание записи
Рис. 3.1 Созданная запись в БД на сайте db4free.net
Рис. 4. Фрагмент работы кода с удаленной БД в Telegram для бота "Сервис создания и отправки уникального пароля для подтверждения платежа на указанный телефонный номер": чтение записи
Рис. 5. Фрагмент работы кода с удаленной БД в Telegram для бота "Сервис создания и отправки уникального пароля для подтверждения платежа на указанный телефонный номер": обновление записи
Рис. 5.1 Обновленная запись в БД на сайте db4free.net
Рис. 6. Фрагмент работы кода с удаленной БД в Telegram для бота "Сервис создания и отправки уникального пароля для подтверждения платежа на указанный телефонный номер":
Вывод: в ходе лабораторной работы были получены навыки работы с библиотекой python-telegram-bot, с помощью которой можно создавать и настраивать боты под свои цели и задачи, был создан бот, который генерирует одноразовый пароль для подтверждения платежа по телефонному номеру, а в ходе генерации кода были применены шаблоны проектирования.
О причинах применения шаблонов проектирования, а именно, о паттернах «Фабричный метод» и «Обозреватель»:
Шаблон "Наблюдатель" позволяет реализовать механизм подписки и уведомления об изменениях в объекте. Сервис создания и отправки индивидуального кода использует этот шаблон для разделения основной функциональности (создание и отправка кода) от дополнительных действий, выполняемых наблюдателями (отправка кода по различным каналам). Новые наблюдатели могут быть легко добавлены без изменения кода сервиса, что обеспечивает гибкость и расширяемость системы.
Шаблон "Фабричный метод" определяет интерфейс для создания объектов, делегируя фактическую реализацию создания объектов подклассам. Это позволяет легко добавлять новые типы объектов, не изменяя существующий код, и обеспечивает гибкость при работе с объектами через общий интерфейс.