initial commit

This commit is contained in:
2025-03-28 13:55:39 +05:00
commit 7f3192f64c
10 changed files with 182 additions and 0 deletions

50
.drone.yml Normal file
View File

@@ -0,0 +1,50 @@
kind: pipeline
type: docker
name: default
steps:
- name: build image
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
environment:
DOCKER_USERNAME:
from_secret: DOCKER_USERNAME
CICD_TOKEN:
from_secret: CICD_TOKEN
commands:
- docker login git.iamninja.ru -u "$DOCKER_USERNAME" -p "$CICD_TOKEN"
- docker build -t git.iamninja.ru/iamninja/pomodoro_tg_bot:latest .
- name: push image
image: plugins/docker
settings:
repo: git.iamninja.ru/iamninja/pomodoro_tg_bot
tags: latest
username:
from_secret: DOCKER_USERNAME
password:
from_secret: CICD_TOKEN
- name: deploy to server
image: appleboy/drone-ssh
settings:
host:
from_secret: DOCKER_DEPLOY
username:
from_secret: DOCKER_USERNAME
port: 22025
key:
from_secret: DOCKER_SSH_KEY
script:
- mkdir -p /home/iamninja/pomodoro_tg_bot
- cd /home/iamninja/pomodoro_tg_bot
- git pull origin main
- docker compose pull
- docker compose up -d
volumes:
- name: dockersock
host:
path: /var/run/docker.sock

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env

9
Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "main.py"]

43
bot.py Normal file
View File

@@ -0,0 +1,43 @@
from aiogram import Bot, Dispatcher, types
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.utils import executor
from aiogram.dispatcher.filters import Text
from user_manager import user_manager
from timer_manager import timer_manager
import os
API_TOKEN = os.getenv("BOT_TOKEN")
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
menu_kb = InlineKeyboardMarkup(row_width=2)
menu_kb.add(
InlineKeyboardButton("▶️ Pomodoro", callback_data="pomodoro"),
InlineKeyboardButton("☕ Short Break", callback_data="shortbreak"),
InlineKeyboardButton("😌 Long Break", callback_data="longbreak"),
InlineKeyboardButton("⏹ Stop", callback_data="stop")
)
@dp.message_handler(commands=['start', 'help'])
async def send_welcome(message: Message):
await message.reply("Привет! Я твой личный Pomodoro-бот. Выбирай действие:", reply_markup=menu_kb)
@dp.callback_query_handler(Text(equals=["pomodoro", "shortbreak", "longbreak", "stop"]))
async def handle_callback(call: types.CallbackQuery):
if call.data == "pomodoro":
await timer_manager.start_timer(call.from_user.id, 25*60, call.message.chat.id, 'Pomodoro')
await call.message.answer("Начался 25-минутный Pomodoro! 🔥")
elif call.data == "shortbreak":
await timer_manager.start_timer(call.from_user.id, 5*60, call.message.chat.id, 'Short Break')
await call.message.answer("Начался 5-минутный перерыв ☕")
elif call.data == "longbreak":
await timer_manager.start_timer(call.from_user.id, 15*60, call.message.chat.id, 'Long Break')
await call.message.answer("Начался длинный перерыв 😌")
elif call.data == "stop":
await timer_manager.stop_timer(call.from_user.id)
await call.message.answer("Таймер остановлен ⏹")
await call.answer()
def run_bot():
executor.start_polling(dp, skip_updates=True)

7
main.py Normal file
View File

@@ -0,0 +1,7 @@
import asyncio
from bot import run_bot
from redis_client import init_redis
if __name__ == '__main__':
asyncio.run(init_redis())
run_bot()

6
models.py Normal file
View File

@@ -0,0 +1,6 @@
from dataclasses import dataclass
@dataclass
class UserState:
current_timer: str = None
task: any = None

10
redis_client.py Normal file
View File

@@ -0,0 +1,10 @@
import aioredis
import os
redis = None
async def init_redis():
global redis
redis = await aioredis.from_url(
os.getenv("REDIS_URL", "redis://localhost"), decode_responses=True
)

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
aiogram==2.25.2
aioredis==2.0.1

34
time_magager.py Normal file
View File

@@ -0,0 +1,34 @@
import asyncio
from user_manager import user_manager
from models import UserState
from aiogram import Bot
import os
bot = Bot(token=os.getenv("BOT_TOKEN"))
class TimerManager:
def __init__(self):
self.tasks = {}
async def start_timer(self, user_id, duration, chat_id, label):
await self.stop_timer(user_id)
async def timer():
await asyncio.sleep(duration)
if label == 'Pomodoro':
await user_manager.increment_pomodoros(user_id)
await bot.send_message(chat_id, f"{label} завершён!")
task = asyncio.create_task(timer())
user = user_manager.get_user(user_id)
user.current_timer = label
user.task = task
self.tasks[user_id] = task
async def stop_timer(self, user_id):
user = user_manager.get_user(user_id)
if user.task:
user.task.cancel()
user.task = None
user.current_timer = None
self.tasks.pop(user_id, None)

20
user_mager.py Normal file
View File

@@ -0,0 +1,20 @@
from models import UserState
from redis_client import redis
class UserManager:
def __init__(self):
self.users = {}
def get_user(self, user_id):
if user_id not in self.users:
self.users[user_id] = UserState()
return self.users[user_id]
async def increment_pomodoros(self, user_id):
await redis.incr(f"user:{user_id}:pomodoros")
async def get_pomodoros(self, user_id):
val = await redis.get(f"user:{user_id}:pomodoros")
return int(val) if val else 0
user_manager = UserManager()