Написала Telegram-бота, который кормит меня по расписанию

Проблема: я настолько увлекаюсь кодингом, что забываю есть. Решение: Python-бот с APScheduler, который стучит в Telegram и не отстаёт, пока я не подтвержу, что поела. Да, я автоматизировала базовые потребности.

Предыстория

Однажды вечером я обнаружила, что последний раз ела в 10 утра, а сейчас уже девять вечера. За это время я написала 400 строк кода, два раза залила всё в git и ни разу не встала из-за стола. Это был знак.

Первая мысль — поставить таймер на телефоне. Скучно. Вторая мысль — написать бота. Вот это уже разговор.

Что в итоге получилось

Бот умеет:

Стек

Всё просто: python-telegram-bot для работы с Bot API, APScheduler для расписания, SQLite через aiosqlite для хранения статистики. Никакого Django, никаких лишних зависимостей.

requirements.txt
python-telegram-bot==21.3
APScheduler==3.10.4
aiosqlite==0.20.0

Основной код

bot.py
import asyncio
from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CallbackQueryHandler
from apscheduler.schedulers.asyncio import AsyncIOScheduler
import aiosqlite, os

TOKEN      = os.environ["BOT_TOKEN"]
MOLLY_ID   = int(os.environ["MOLLY_CHAT_ID"])
REMINDER_TIMES = [(8, 0), (13, 0), (19, 0)]

async def send_reminder(bot: Bot):
    keyboard = InlineKeyboardMarkup([[
        InlineKeyboardButton("Поела ✓", callback_data="ate"),
        InlineKeyboardButton("Потом 😿", callback_data="later"),
    ]])
    await bot.send_message(
        chat_id=MOLLY_ID,
        text="🍽 Молли, стоп. Брось компьютер. Поешь.",
        reply_markup=keyboard,
    )

async def handle_button(update, context):
    query = update.callback_query
    await query.answer()
    if query.data == "ate":
        await query.edit_message_text("Умница! 🐾 +1 к здоровью")
        await log_meal()
    else:
        await query.edit_message_text("Хорошо, но через 15 минут спрошу ещё раз 😾")

async def log_meal():
    async with aiosqlite.connect("meals.db") as db:
        await db.execute("INSERT INTO meals (ts) VALUES (datetime('now'))")
        await db.commit()

Запуск по расписанию

bot.py (продолжение)
def main():
    app = Application.builder().token(TOKEN).build()
    app.add_handler(CallbackQueryHandler(handle_button))

    scheduler = AsyncIOScheduler()
    for hour, minute in REMINDER_TIMES:
        scheduler.add_job(
            send_reminder,
            trigger="cron",
            hour=hour, minute=minute,
            args=[app.bot],
        )
    scheduler.start()
    app.run_polling()

if __name__ == "__main__":
    main()
⚠️ Важный момент
Храните BOT_TOKEN и MOLLY_CHAT_ID в переменных окружения, никогда не в коде. Я использую .env файл + python-dotenv локально и секреты systemd в продакшене.

Деплой

Бот крутится на самом дешёвом VPS в виде systemd-сервиса. Без Docker, без Kubernetes — просто bot.service и автозапуск. Работает уже три месяца без единого сбоя.

Результат

За последние три месяца я пропустила только 4 приёма пищи из 270. Это 98.5% успешности. Мой желудок доволен. Бот доволен. Я доволена.

Код лежит на GitHub, если хотите форкнуть и адаптировать под себя 🐾

Была ли статья полезной?