Inform Bot - PRO100 Blog

Inform Bot

Приветствую в своём блоге, сегодня я расскажу вам о своём небольшом проекте. Недавно я уже писал, про этого бота, это был первый проект такого плана. До этого я никогда раньше не писал ботов, поэтому этот проект, так называемая, "проба пера") В этом обновлении я добавил базу данных к боту, вместо файла json. Сейчас мы попробуем разобрать весь код подробнее. 

 

Структура проекта:

-data/

    -db.db

-.env

-bot.py

-btns.py

-db_tool.py

-parse_func.py

-requirements.txt

Немного расскажу о структуре проекта, имеем папку data/ в ней лежит файл базы данных db.db, БД использовал SQLite, она отлично подошла для такого небольшого проекта.

Файл .env содержит переменные окружения, там у нас записан токен бота, токен Open Weather Map и директория, из которой подтягивается БД.

Файл bot.py содержит основной код программы, btns.py - кнопки для бота решил поместить в отдельный файл, чтобы разгрузить основной.

Файл db_tool.py содержит функции для работы с базой данных, parse_func.py - функции парсинга данных.

Файл requirements.txt - зависимости.

 

Код

Теперь покажу немного кода. Весь код будет доступен по ссылке, здесь покажу только основные моменты.

 

bot.py

# Создаем бота
bot = telebot.TeleBot(config('API_TOKEN'))

Функция для проверки событий в другом потоке:

def schedule_checker():
    while True:
        try:
            schedule.run_pending()
            sleep(2)
        except Exception as err:
            print(f'{datetime.now().strftime("%d/%m/%Y %H:%M")} - {err}')

В этой функции мы получаем всех пользователей из БД, а затем получаем значение, настроенного времени с текущим, если совпадает отправляем ежедневную сводку. В коде есть замечание, я получаю текущее время и прибавляю к нему timedelta, это из-за часовых поясов, у меня на сервере -3 от Москвы.

def check_time():
    try:
        for user in get_users():
            try:
                if get_values(user)['time'] == (datetime.now() + timedelta(hours=3)).strftime('%H:%M'):
                    daily_notif(user)
            except telebot.apihelper.ApiTelegramException as err:
                if 'bot was blocked by the user' in err.description:
                    delete(user)
    except Exception as err:
        print(f'{datetime.now().strftime("%d/%m/%Y %H:%M")} - {err}')

Функция для получения значений, записанных в БД и отправка пользователю:

def bot_info(message):
    markup = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    markup.add(buttons['settings_btn'])
    markup.add(buttons['info_btn'])
    values = get_values(message.chat.id)
    bot.send_message(chat_id=message.chat.id,
                     text=f'Настройки бота:\n\n'
                          f'⏰ Время: {values["time"]}\n'
                          f'🏰 Город: {values["city"]}\n'
                          f'🌤 Сводка о погоде: {values["weather"]}\n'
                          f'💰 Курсы валют: {values["currency"]}\n'
                          f'📖 Цитата: {values["quote"]}',
                     reply_markup=markup)

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

if __name__ == '__main__':
    print('Запуск бота..')

    schedule.every().minute.do(check_time)
    Thread(target=schedule_checker, daemon=True).start()

Обрабатываем команду /start:

@bot.message_handler(commands=['start'])
    def send_welcome(message):
        if not str(message.chat.id) in get_users():
            add(message.chat.id, '07:00', 'Москва', 'вкл', 'вкл', 'вкл')
        markup = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
        markup.add(buttons['settings_btn'])
        markup.add(buttons['info_btn'])
        bot.send_message(message.chat.id, 'Привет! Это бот-информатор.\n'
                                          'Он присылает сообщение каждый день в одно и тоже время.\nСодержание сообщения и время определяет пользователь.',
 reply_markup=markup)

Дальше идёт обработка текстовых сообщений пользователя, а именно какие кнопки были нажаты, для примера кнопка настроек:

match message.text:
            case 'ℹ️Инфоℹ️':
                bot_info(message)
            case '🛠Настройки🛠':
                markup = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
                markup.add(buttons['city_btn'], buttons_list[2], buttons_list[1], buttons_list[0],
 buttons['time_btn'],
 buttons['main_menu_btn'])
                bot.send_message(message.chat.id, text='Для настройки параметра, кликни на кнопку.', reply_markup=markup)

Запускаем бесконечную прослушку бота:

bot.infinity_polling()

С основными моментами в файле bot.py, вроде всё.

btns.py

from telebot.types import KeyboardButton

from db_tool import get_values

# Кнопки
buttons = {
    'settings_btn': KeyboardButton('🛠Настройки🛠'),
    'city_btn': KeyboardButton('🏰Город🏰'),
    'weath_btn_on': KeyboardButton('🌤Погода✅'),
    'weath_btn_off': KeyboardButton('🌤Погода❎'),
    'curr_btn_on': KeyboardButton('💰Курс✅'),
    'curr_btn_off': KeyboardButton('💰Курс❎'),
    'quote_btn_on': KeyboardButton('📖Цитата✅'),
    'quote_btn_off': KeyboardButton('📖Цитата❎'),
    'time_btn': KeyboardButton('⏰Время⏰'),
    'main_menu_btn': KeyboardButton('🔝Главная🔝'),
    'info_btn': KeyboardButton('ℹ️Инфоℹ️'),
}


# Получаем состояние кнопок
def get_buttons(chat_id):
    buttons_list = []
    values = get_values(chat_id)
    match values['weather']:
        case 'вкл':
            weather_btn = buttons['weath_btn_on']
            buttons_list.append(weather_btn)
        case 'выкл':
            weather_btn = buttons['weath_btn_off']
            buttons_list.append(weather_btn)
    match values['quote']:
        case 'вкл':
            quote_btn = buttons['quote_btn_on']
            buttons_list.append(quote_btn)
        case 'выкл':
            quote_btn = buttons['quote_btn_off']
            buttons_list.append(quote_btn)
    match values['currency']:
        case 'вкл':
            cur_btn = buttons['curr_btn_on']
            buttons_list.append(cur_btn)
        case 'выкл':
            cur_btn = buttons['curr_btn_off']
            buttons_list.append(cur_btn)
    # Возвращаем список состояний кнопок
    return buttons_list

Больше всего было интересно работать с БД, написал функции для работы с ней. Вот пример функции для получения значений по chat_id пользователя:

db_tools.py

def get_values(chat_id):
    with sqlite3.connect(f'{config("DIRECTORY")}/db.db') as conn:
        cur = conn.cursor()
        try:
            with conn:
                cur.execute('SELECT * FROM Users WHERE chat_id = ?', (f'{chat_id}',))
                user = cur.fetchone()
                return {
                    'chat_id': user[1],
                    'time': user[2],
                    'city': user[3],
                    'weather': user[4],
                    'quote': user[5],
                    'currency': user[6],
                }
        except Exception as err:
            print(f'{datetime.now().strftime("%d/%m/%Y %H:%M")} - {err}')

В результате получаем словарь со всеми нужными значениями.

 

Про парсинг писать особо нечего, ибо там все просто, кому интересно, можете глянуть код на GitFlic.

 

P.S. Вы знали, что у нас есть российский аналог GitHub? Я не знал, а теперь знаю, так что проекты буду там свои выкладывать отныне. Поддержим российского производителя))

Ссылка на проект