mirror of
https://github.com/EDeev/y.calendarkin.git
synced 2026-06-18 14:01:03 +03:00
Merge 99e1793759 into a7eeff23c1
This commit is contained in:
commit
13bde5c495
11 changed files with 1671 additions and 586 deletions
63
.dockerignore
Normal file
63
.dockerignore
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile
|
||||||
|
docker-compose.yml
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
env.example
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
README.md
|
||||||
|
*.md
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
db/
|
||||||
|
.env
|
||||||
33
Dockerfile
Normal file
33
Dockerfile
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Используем официальный Python образ
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Устанавливаем рабочую директорию
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Устанавливаем системные зависимости
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
wget \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Копируем файл зависимостей
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Устанавливаем Python зависимости
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Копируем код приложения
|
||||||
|
COPY code/ ./code/
|
||||||
|
COPY data/ ./data/
|
||||||
|
|
||||||
|
# Создаем директории для базы данных
|
||||||
|
RUN mkdir -p db
|
||||||
|
|
||||||
|
# Устанавливаем рабочую директорию для запуска
|
||||||
|
WORKDIR /app/code
|
||||||
|
|
||||||
|
# Открываем порт (если потребуется для мониторинга)
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Команда запуска
|
||||||
|
CMD ["python", "bot.py"]
|
||||||
|
|
||||||
54
README.md
54
README.md
|
|
@ -30,13 +30,47 @@ Y.Calendarkin — некоммерческий проект, предназна
|
||||||
|
|
||||||
## 🚀 Установка и запуск
|
## 🚀 Установка и запуск
|
||||||
|
|
||||||
### Предварительные требования
|
### 🐳 Запуск с Docker (Рекомендуется)
|
||||||
|
|
||||||
|
1. **Клонируйте репозиторий:**
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd y.calendarkin
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Создайте файл с переменными окружения:**
|
||||||
|
```bash
|
||||||
|
cp env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Настройте переменные окружения:**
|
||||||
|
- Откройте файл `.env`
|
||||||
|
- Замените `your_bot_token_here` на токен вашего бота от [@BotFather](https://t.me/BotFather)
|
||||||
|
|
||||||
|
4. **Запустите бота:**
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Проверьте логи:**
|
||||||
|
```bash
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Остановите бота:**
|
||||||
|
```bash
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📦 Локальная установка
|
||||||
|
|
||||||
|
#### Предварительные требования
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Настройка
|
#### Настройка
|
||||||
|
|
||||||
1. Создайте нового бота через [@BotFather](https://t.me/BotFather)
|
1. Создайте нового бота через [@BotFather](https://t.me/BotFather)
|
||||||
2. Получите токен и добавьте его в `config.py`:
|
2. Получите токен и добавьте его в `config.py`:
|
||||||
|
|
@ -51,7 +85,7 @@ TOKEN = "your_bot_token_here"
|
||||||
mkdir -p data/icals db
|
mkdir -p data/icals db
|
||||||
```
|
```
|
||||||
|
|
||||||
### Запуск
|
#### Запуск
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd code
|
cd code
|
||||||
|
|
@ -100,9 +134,23 @@ y.calendarkin/
|
||||||
│ ├── icals/ # Загруженные календари
|
│ ├── icals/ # Загруженные календари
|
||||||
│ └── photo_edit_alarm.jpg
|
│ └── photo_edit_alarm.jpg
|
||||||
├── db/ # База данных SQLite
|
├── db/ # База данных SQLite
|
||||||
|
├── Dockerfile # Docker образ
|
||||||
|
├── docker-compose.yml # Docker Compose конфигурация
|
||||||
|
├── .dockerignore # Исключения для Docker
|
||||||
|
├── env.example # Пример переменных окружения
|
||||||
└── requirements.txt
|
└── requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Управление данными
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Создание резервной копии базы данных
|
||||||
|
docker cp y-calendarkin-bot:/app/db ./backup/
|
||||||
|
|
||||||
|
# Восстановление базы данных
|
||||||
|
docker cp ./backup/db y-calendarkin-bot:/app/
|
||||||
|
```
|
||||||
|
|
||||||
## 📊 База данных
|
## 📊 База данных
|
||||||
|
|
||||||
Проект использует две основные таблицы:
|
Проект использует две основные таблицы:
|
||||||
|
|
|
||||||
647
code/bot.py
647
code/bot.py
|
|
@ -1,321 +1,326 @@
|
||||||
import logging, config, sql, asyncio, wget, os
|
import logging, config, sql, asyncio, wget, os
|
||||||
|
|
||||||
from aiogram import Bot, Dispatcher, executor, types
|
from aiogram import Bot, Dispatcher, types, F
|
||||||
from aiogram.types import ParseMode
|
from aiogram.enums import ParseMode
|
||||||
|
from aiogram.filters import Command, or_f
|
||||||
from datetime import datetime, time
|
|
||||||
from script import text_ical, message_form, dt_now, delta_time
|
from datetime import datetime, time
|
||||||
|
from script import text_ical, message_form, dt_now, delta_time
|
||||||
# инициализируем токен
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
# инициализируем токен
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
bot = Bot(token=config.TOKEN)
|
|
||||||
dp = Dispatcher(bot)
|
bot = Bot(token=config.TOKEN)
|
||||||
|
dp = Dispatcher()
|
||||||
# инициализируем соединение с БД
|
|
||||||
du = sql.Users('../db/users.db')
|
# инициализируем соединение с БД
|
||||||
dc = sql.Clock('../db/clock.db')
|
du = sql.Users('../db/users.db')
|
||||||
|
dc = sql.Clock('../db/clock.db')
|
||||||
|
|
||||||
# ПРИВЕТСТВЕННОЕ СООБЩЕНИЕ
|
|
||||||
@dp.message_handler(commands=['start', 'help'])
|
# ПРИВЕТСТВЕННОЕ СООБЩЕНИЕ
|
||||||
async def helps(message: types.Message):
|
@dp.message(or_f(Command('start'), Command('help')))
|
||||||
tg_id = int(message.chat.id)
|
async def helps(message: types.Message):
|
||||||
if not du.user_exists(tg_id):
|
tg_id = int(message.chat.id)
|
||||||
du.add_user(tg_id)
|
if not du.user_exists(tg_id):
|
||||||
|
du.add_user(tg_id)
|
||||||
buttons = [types.InlineKeyboardButton(text="КОМАНДЫ", callback_data="com"),
|
|
||||||
types.InlineKeyboardButton(text="АВТОР", callback_data="auth")]
|
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[types.InlineKeyboardButton(text="КОМАНДЫ", callback_data="com"),
|
||||||
keyboard = types.InlineKeyboardMarkup(row_width=2)
|
types.InlineKeyboardButton(text="АВТОР", callback_data="auth")]
|
||||||
keyboard.add(*buttons)
|
])
|
||||||
|
|
||||||
await message.answer(text="<b>Я.Календаркин</b> - бот для оповещения о событиях из <b>Яндекс.Календаря</b>. "
|
await message.answer(text="<b>Я.Календаркин</b> - бот для оповещения о событиях из <b>Яндекс.Календаря</b>. "
|
||||||
"Для начала работы вам нужно всего лишь прислать в чат ссылку экспорта календаря в "
|
"Для начала работы вам нужно всего лишь прислать в чат ссылку экспорта календаря в "
|
||||||
"<b>формате ICal</b>. После получения ссылки, бот начнёт оповещать о всех новых событиях "
|
"<b>формате ICal</b>. После получения ссылки, бот начнёт оповещать о всех новых событиях "
|
||||||
"и появится возможность настройки оповещений. О том, какие команды есть для настройки, "
|
"и появится возможность настройки оповещений. О том, какие команды есть для настройки, "
|
||||||
"вы можете ознакомиться по кнопке <b>КОМАНДЫ</b>",
|
"вы можете ознакомиться по кнопке <b>КОМАНДЫ</b>",
|
||||||
parse_mode=ParseMode.HTML, reply_markup=keyboard)
|
parse_mode=ParseMode.HTML, reply_markup=keyboard)
|
||||||
|
|
||||||
|
|
||||||
@dp.callback_query_handler(text="auth")
|
@dp.callback_query(F.data == "auth")
|
||||||
async def author(call: types.CallbackQuery):
|
async def author(call: types.CallbackQuery):
|
||||||
await call.message.answer(text='*| АВТОР |*\n\n*>>* Этот бот не коммерческий проект, для упрощенного получения '
|
await call.message.answer(text='*| АВТОР |*\n\n*>>* Этот бот не коммерческий проект, для упрощенного получения '
|
||||||
'уведомлений о событиях в Яндекс.Календаре. Не многим этот бот будет полезен, но '
|
'уведомлений о событиях в Яндекс.Календаре. Не многим этот бот будет полезен, но '
|
||||||
'людям, чья работа подразумевает его использование, он станет лишь удобным '
|
'людям, чья работа подразумевает его использование, он станет лишь удобным '
|
||||||
'инструментом. Я же пишу подобные небольшие проекты, о которых вы можете узнать '
|
'инструментом. Я же пишу подобные небольшие проекты, о которых вы можете узнать '
|
||||||
'больше на моём [GitHub](https://github.com/IGlek).',
|
'больше на моём [GitHub](https://github.com/IGlek).',
|
||||||
parse_mode=ParseMode.MARKDOWN)
|
parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
|
||||||
@dp.callback_query_handler(text="com")
|
@dp.callback_query(F.data == "com")
|
||||||
async def commands(call: types.CallbackQuery):
|
async def commands(call: types.CallbackQuery):
|
||||||
await call.message.answer(text='<b>| КОМАНДЫ |</b>\n\n'
|
await call.message.answer(text='<b>| КОМАНДЫ |</b>\n\n'
|
||||||
'<b>/help</b> - вспомогательная функция для уточнения работы команд\n'
|
'<b>/help</b> - вспомогательная функция для уточнения работы команд\n'
|
||||||
'<b>/list</b> - список событий календаря, запланированных на сегодняшний день\n'
|
'<b>/list</b> - список событий календаря, запланированных на сегодняшний день\n'
|
||||||
'<b>/notif</b> - команда, отключающая рассылку уведомлений, даже при наличии событий в календаре\n'
|
'<b>/notif</b> - команда, отключающая рассылку уведомлений, даже при наличии событий в календаре\n'
|
||||||
'<b>/daily</b> - оповещение в 8 утра по вашему часовому поясу со списком событий на день\n'
|
'<b>/daily</b> - оповещение в 8 утра по вашему часовому поясу со списком событий на день\n'
|
||||||
'<b>/moment</b> - напоминание, приходящее в момент начала события\n\n'
|
'<b>/moment</b> - напоминание, приходящее в момент начала события\n\n'
|
||||||
'<b>/get_alarm</b> - информация о времени на которое настроены оповещения\n'
|
'<b>/get_alarm</b> - информация о времени на которое настроены оповещения\n'
|
||||||
'<b>/edit_alarm</b> - изменение времени оповещений\n'
|
'<b>/edit_alarm</b> - изменение времени оповещений\n'
|
||||||
'<b>/stop_alarm</b> - команда, отключающая второе оповещение о событии',
|
'<b>/stop_alarm</b> - команда, отключающая второе оповещение о событии',
|
||||||
parse_mode=types.ParseMode.HTML)
|
parse_mode=ParseMode.HTML)
|
||||||
|
|
||||||
|
|
||||||
# КОМАНДЫ
|
# КОМАНДЫ
|
||||||
@dp.message_handler(commands=['list'])
|
@dp.message(Command('list'))
|
||||||
async def check_list(message: types.Message):
|
async def check_list(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if du.url_exists(user_id):
|
if du.url_exists(user_id):
|
||||||
txt = "<b>Имеющиеся события на сегодня</b>\n\n"
|
txt = "<b>Имеющиеся события на сегодня</b>\n\n"
|
||||||
|
|
||||||
lst_events = sorted(text_ical(user_id, du.get_tz(user_id)))
|
lst_events = sorted(text_ical(user_id, du.get_tz(user_id)))
|
||||||
today = dt_now(du.get_tz(user_id)).date()
|
today = dt_now(du.get_tz(user_id)).date()
|
||||||
|
|
||||||
counter = 0
|
counter = 0
|
||||||
for event in lst_events:
|
for event in lst_events:
|
||||||
if event[0] == today:
|
if event[0] == today:
|
||||||
counter += 1
|
counter += 1
|
||||||
txt += message_form(counter, event[3])
|
txt += message_form(counter, event[3])
|
||||||
|
|
||||||
if counter:
|
if counter:
|
||||||
await message.answer(text=txt, parse_mode=ParseMode.HTML)
|
await message.answer(text=txt, parse_mode=ParseMode.HTML)
|
||||||
else:
|
else:
|
||||||
await message.answer(text="<b>В данный момент</b> событий на сегодня найдено не было!",
|
await message.answer(text="<b>В данный момент</b> событий на сегодня найдено не было!",
|
||||||
parse_mode=ParseMode.HTML)
|
parse_mode=ParseMode.HTML)
|
||||||
else:
|
else:
|
||||||
await message.answer("Для отображения событий вы должны прислать ical-ссылку на календарь!")
|
await message.answer("Для отображения событий вы должны прислать ical-ссылку на календарь!")
|
||||||
|
|
||||||
|
|
||||||
@dp.message_handler(commands=['notif'])
|
@dp.message(Command('notif'))
|
||||||
async def notif_up(message: types.Message):
|
async def notif_up(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if du.url_exists(user_id):
|
if du.url_exists(user_id):
|
||||||
if du.get_status(user_id):
|
if du.get_status(user_id):
|
||||||
await message.answer("Уведомления о событиях выключены!")
|
await message.answer("Уведомления о событиях выключены!")
|
||||||
else:
|
else:
|
||||||
await message.answer("Уведомления о событиях включены!")
|
await message.answer("Уведомления о событиях включены!")
|
||||||
|
|
||||||
du.update_status(user_id)
|
du.update_status(user_id)
|
||||||
else:
|
else:
|
||||||
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
||||||
|
|
||||||
|
|
||||||
@dp.message_handler(commands=['daily'])
|
@dp.message(Command('daily'))
|
||||||
async def daily_up(message: types.Message):
|
async def daily_up(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if dc.clock_exists(user_id):
|
if dc.clock_exists(user_id):
|
||||||
if dc.get_daily(user_id):
|
if dc.get_daily(user_id):
|
||||||
await message.answer("Ежедневные утренние уведомления выключены!")
|
await message.answer("Ежедневные утренние уведомления выключены!")
|
||||||
else:
|
else:
|
||||||
await message.answer("Ежедневные утренние уведомления включены!")
|
await message.answer("Ежедневные утренние уведомления включены!")
|
||||||
|
|
||||||
dc.update_daily(user_id)
|
dc.update_daily(user_id)
|
||||||
else:
|
else:
|
||||||
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
||||||
|
|
||||||
|
|
||||||
@dp.message_handler(commands=['moment'])
|
@dp.message(Command('moment'))
|
||||||
async def start_up(message: types.Message):
|
async def start_up(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if dc.clock_exists(user_id):
|
if dc.clock_exists(user_id):
|
||||||
if dc.get_start(user_id):
|
if dc.get_start(user_id):
|
||||||
await message.answer("Уведомления в момент события выключены!")
|
await message.answer("Уведомления в момент события выключены!")
|
||||||
else:
|
else:
|
||||||
await message.answer("Уведомления в момент события включены!")
|
await message.answer("Уведомления в момент события включены!")
|
||||||
|
|
||||||
dc.update_start(user_id)
|
dc.update_start(user_id)
|
||||||
else:
|
else:
|
||||||
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
||||||
|
|
||||||
|
|
||||||
@dp.message_handler(commands=['get_alarm'])
|
@dp.message(Command('get_alarm'))
|
||||||
async def start_up(message: types.Message):
|
async def start_up(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if dc.clock_exists(user_id):
|
if dc.clock_exists(user_id):
|
||||||
alarms = dc.get_alarm(user_id)
|
alarms = dc.get_alarm(user_id)
|
||||||
start = dc.get_start(user_id)
|
start = dc.get_start(user_id)
|
||||||
status2 = dc.get_status2(user_id)
|
status2 = dc.get_status2(user_id)
|
||||||
|
|
||||||
txt = ""
|
txt = ""
|
||||||
|
|
||||||
if status2:
|
if status2:
|
||||||
txt += "<b>У вас работает два оповещения"
|
txt += "<b>У вас работает два оповещения"
|
||||||
else:
|
else:
|
||||||
txt += "<b>У вас работает лишь первое оповещение"
|
txt += "<b>У вас работает лишь первое оповещение"
|
||||||
|
|
||||||
if start:
|
if start:
|
||||||
txt += " и сообщение в момент начала события!</b>"
|
txt += " и сообщение в момент начала события!</b>"
|
||||||
else:
|
else:
|
||||||
txt += "!</b>"
|
txt += "!</b>"
|
||||||
|
|
||||||
await message.answer(text=(txt + f"\n\n<b>Первое оповещение</b> приходит за {alarms[0]} минут\n"
|
await message.answer(text=(txt + f"\n\n<b>Первое оповещение</b> приходит за {alarms[0]} минут\n"
|
||||||
f"<b>Второе оповещение</b> приходит за {alarms[1]} минут"),
|
f"<b>Второе оповещение</b> приходит за {alarms[1]} минут"),
|
||||||
parse_mode=ParseMode.HTML)
|
parse_mode=ParseMode.HTML)
|
||||||
else:
|
else:
|
||||||
await message.answer("Для того, чтобы получить таймеры, вы должны прислать ical-ссылку на свой календарь!")
|
await message.answer("Для того, чтобы получить таймеры, вы должны прислать ical-ссылку на свой календарь!")
|
||||||
|
|
||||||
|
|
||||||
@dp.message_handler(commands=['edit_alarm'])
|
@dp.message(Command('edit_alarm'))
|
||||||
async def start_up(message: types.Message):
|
async def start_up(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if dc.clock_exists(user_id):
|
if dc.clock_exists(user_id):
|
||||||
await message.bot.send_photo(chat_id=message.chat.id, photo=open("../data/photo_edit_alarm.jpg", "rb"),
|
await message.bot.send_photo(chat_id=message.chat.id, photo=open("../data/photo_edit_alarm.jpg", "rb"),
|
||||||
caption="Для изменения времени вам надо в ответ на это сообщение прислать два "
|
caption="Для изменения времени вам надо в ответ на это сообщение прислать два "
|
||||||
"числа через пробел: разница времени первого и второго таймера по ходу "
|
"числа через пробел: разница времени первого и второго таймера по ходу "
|
||||||
"времени соответственно")
|
"времени соответственно")
|
||||||
else:
|
else:
|
||||||
await message.answer("Для того, чтобы изменить таймеры, вы должны прислать ical-ссылку на свой календарь!")
|
await message.answer("Для того, чтобы изменить таймеры, вы должны прислать ical-ссылку на свой календарь!")
|
||||||
|
|
||||||
|
|
||||||
@dp.message_handler(commands=['stop_alarm'])
|
@dp.message(Command('stop_alarm'))
|
||||||
async def daily_up(message: types.Message):
|
async def daily_up(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if dc.clock_exists(user_id):
|
if dc.clock_exists(user_id):
|
||||||
if dc.get_status2(user_id):
|
if dc.get_status2(user_id):
|
||||||
await message.answer("Второе уведомление выключено!")
|
await message.answer("Второе уведомление выключено!")
|
||||||
else:
|
else:
|
||||||
await message.answer("Второе уведомление включено!")
|
await message.answer("Второе уведомление включено!")
|
||||||
|
|
||||||
dc.update_status2(user_id)
|
dc.update_status2(user_id)
|
||||||
else:
|
else:
|
||||||
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
|
||||||
|
|
||||||
|
|
||||||
# ЗАГРУЗКА ССЫЛКИ
|
# ЗАГРУЗКА ССЫЛКИ
|
||||||
@dp.message_handler(content_types=['text'])
|
@dp.message(F.text)
|
||||||
async def downloading_file_ics(message: types.Message):
|
async def downloading_file_ics(message: types.Message):
|
||||||
user_id = du.get_user_id(int(message.chat.id))
|
user_id = du.get_user_id(int(message.chat.id))
|
||||||
|
|
||||||
if message.text[:5] == "https":
|
if message.text[:5] == "https":
|
||||||
try:
|
try:
|
||||||
wget.download(message.text, f'../data/icals/{str(user_id)}_new.ics')
|
wget.download(message.text, f'../data/icals/{str(user_id)}_new.ics')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.remove(f'../data/icals/{str(user_id)}.ics')
|
os.remove(f'../data/icals/{str(user_id)}.ics')
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
os.rename(f'../data/icals/{str(user_id)}_new.ics', f'../data/icals/{str(user_id)}.ics')
|
os.rename(f'../data/icals/{str(user_id)}_new.ics', f'../data/icals/{str(user_id)}.ics')
|
||||||
|
|
||||||
time_zone = message.text.split("=")[-1]
|
time_zone = message.text.split("=")[-1]
|
||||||
if not du.url_exists(user_id):
|
if not du.url_exists(user_id):
|
||||||
du.add_url(user_id, message.text, time_zone)
|
du.add_url(user_id, message.text, time_zone)
|
||||||
dc.add_clock(user_id)
|
dc.add_clock(user_id)
|
||||||
else:
|
else:
|
||||||
du.update_url(user_id, message.text, time_zone)
|
du.update_url(user_id, message.text, time_zone)
|
||||||
|
|
||||||
await message.answer("Ссылка успешно добавлена! Уведомления уже включены!")
|
await message.answer("Ссылка успешно добавлена! Уведомления уже включены!")
|
||||||
except Exception:
|
except Exception:
|
||||||
await message.answer("Ошибка скачивания! Проверьте правильность ссылки и пришлите ещё раз")
|
await message.answer("Ошибка скачивания! Проверьте правильность ссылки и пришлите ещё раз")
|
||||||
|
|
||||||
if 'reply_to_message' in message and dc.clock_exists(user_id):
|
if 'reply_to_message' in message and dc.clock_exists(user_id):
|
||||||
text = "Для изменения времени вам надо в ответ на это сообщение прислать два числа через " \
|
text = "Для изменения времени вам надо в ответ на это сообщение прислать два числа через " \
|
||||||
"пробел: разница времени первого и второго таймера по ходу времени соответственно"
|
"пробел: разница времени первого и второго таймера по ходу времени соответственно"
|
||||||
|
|
||||||
if message.reply_to_message.caption == text:
|
if message.reply_to_message.caption == text:
|
||||||
alarm_new = message.text.split()
|
alarm_new = message.text.split()
|
||||||
|
|
||||||
if int(alarm_new[0]) < 60 and int(alarm_new[1]) < 60:
|
if int(alarm_new[0]) < 60 and int(alarm_new[1]) < 60:
|
||||||
dc.update_alarm1(user_id, int(alarm_new[0]))
|
dc.update_alarm1(user_id, int(alarm_new[0]))
|
||||||
dc.update_alarm2(user_id, int(alarm_new[1]))
|
dc.update_alarm2(user_id, int(alarm_new[1]))
|
||||||
|
|
||||||
await message.answer("Время отправки уведомлений успешно обновлено!")
|
await message.answer("Время отправки уведомлений успешно обновлено!")
|
||||||
else:
|
else:
|
||||||
await message.answer("Время отправки уведомлений должно быть меньше 60 минут!")
|
await message.answer("Время отправки уведомлений должно быть меньше 60 минут!")
|
||||||
|
|
||||||
|
|
||||||
# ПРОВЕРКА НА СОБЫТИЕ
|
# ПРОВЕРКА НА СОБЫТИЕ
|
||||||
async def alarm(wait_for):
|
async def alarm(wait_for):
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(wait_for)
|
await asyncio.sleep(wait_for)
|
||||||
|
|
||||||
users_id = du.all_users()
|
users_id = du.all_users()
|
||||||
for user_id in users_id:
|
for user_id in users_id:
|
||||||
user_id = user_id[0]
|
user_id = user_id[0]
|
||||||
|
|
||||||
if dc.clock_exists(user_id):
|
if dc.clock_exists(user_id):
|
||||||
if du.get_status(user_id):
|
if du.get_status(user_id):
|
||||||
events = sorted(text_ical(user_id, du.get_tz(user_id)))
|
events = sorted(text_ical(user_id, du.get_tz(user_id)))
|
||||||
tg_id = str(du.get_first_user_id(user_id))
|
tg_id = str(du.get_first_user_id(user_id))
|
||||||
|
|
||||||
start = dc.get_start(user_id)
|
start = dc.get_start(user_id)
|
||||||
daily = dc.get_daily(user_id)
|
daily = dc.get_daily(user_id)
|
||||||
alarm = dc.get_alarm(user_id)
|
alarm = dc.get_alarm(user_id)
|
||||||
|
|
||||||
alarm2_status = dc.get_status2(user_id)
|
alarm2_status = dc.get_status2(user_id)
|
||||||
|
|
||||||
today = dt_now(du.get_tz(user_id)).date()
|
today = dt_now(du.get_tz(user_id)).date()
|
||||||
time_check = dt_now(du.get_tz(user_id)).time()
|
time_check = dt_now(du.get_tz(user_id)).time()
|
||||||
|
|
||||||
# ДЛЯ DAILY
|
# ДЛЯ DAILY
|
||||||
counter = 0
|
counter = 0
|
||||||
txt = "<b>События сегодня</b>\n\n"
|
txt = "<b>События сегодня</b>\n\n"
|
||||||
|
|
||||||
delta_daily1 = time(hour=8, minute=0)
|
delta_daily1 = time(hour=8, minute=0)
|
||||||
delta_daily2 = time(hour=8, minute=1)
|
delta_daily2 = time(hour=8, minute=1)
|
||||||
# --------------------------------
|
# --------------------------------
|
||||||
|
|
||||||
for event in events:
|
for event in events:
|
||||||
if event[0] == today:
|
if event[0] == today:
|
||||||
d_event = datetime.combine(today, event[1])
|
d_event = datetime.combine(today, event[1])
|
||||||
|
|
||||||
if daily and delta_daily1 <= time_check < delta_daily2:
|
if daily and delta_daily1 <= time_check < delta_daily2:
|
||||||
counter += 1
|
counter += 1
|
||||||
txt += message_form(counter, event[3])
|
txt += message_form(counter, event[3])
|
||||||
|
|
||||||
delta_start = delta_time(d_event, 1, 0)
|
delta_start = delta_time(d_event, 1, 0)
|
||||||
if start and delta_start[0] < time_check <= delta_start[1]:
|
if start and delta_start[0] < time_check <= delta_start[1]:
|
||||||
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
||||||
text=f"<b>Событие начинается!</b>\n\n" + message_form(0, event[3]))
|
text=f"<b>Событие начинается!</b>\n\n" + message_form(0, event[3]))
|
||||||
|
|
||||||
delta_alarm1 = delta_time(d_event, alarm[0], alarm[0] - 1)
|
delta_alarm1 = delta_time(d_event, alarm[0], alarm[0] - 1)
|
||||||
if delta_alarm1[0] < time_check <= delta_alarm1[1]:
|
if delta_alarm1[0] < time_check <= delta_alarm1[1]:
|
||||||
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
||||||
text=f"<b>Напоминаю!</b>\n<i>Через {alarm[0]} минут "
|
text=f"<b>Напоминаю!</b>\n<i>Через {alarm[0]} минут "
|
||||||
f"будет событие:</i>\n\n{message_form(0, event[3])}")
|
f"будет событие:</i>\n\n{message_form(0, event[3])}")
|
||||||
|
|
||||||
if alarm2_status:
|
if alarm2_status:
|
||||||
delta_alarm2 = delta_time(d_event, alarm[1], alarm[1] - 1)
|
delta_alarm2 = delta_time(d_event, alarm[1], alarm[1] - 1)
|
||||||
if delta_alarm2[0] < time_check <= delta_alarm2[1]:
|
if delta_alarm2[0] < time_check <= delta_alarm2[1]:
|
||||||
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
||||||
text=f"<b>Напоминаю!</b>\n<i>Через {alarm[1]} минут "
|
text=f"<b>Напоминаю!</b>\n<i>Через {alarm[1]} минут "
|
||||||
f"будет событие:</i>\n\n{message_form(0, event[3])}")
|
f"будет событие:</i>\n\n{message_form(0, event[3])}")
|
||||||
|
|
||||||
if daily and delta_daily1 <= time_check < delta_daily2:
|
if daily and delta_daily1 <= time_check < delta_daily2:
|
||||||
if counter:
|
if counter:
|
||||||
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
||||||
text=txt)
|
text=txt)
|
||||||
else:
|
else:
|
||||||
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
|
||||||
text=f"Сегодня событий <b>нет</b>")
|
text=f"Сегодня событий <b>нет</b>")
|
||||||
|
|
||||||
|
|
||||||
async def update(wait_for):
|
async def update(wait_for):
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(wait_for)
|
await asyncio.sleep(wait_for)
|
||||||
|
|
||||||
users_id = du.all_users()
|
users_id = du.all_users()
|
||||||
for user_id in users_id:
|
for user_id in users_id:
|
||||||
user_id = user_id[0]
|
user_id = user_id[0]
|
||||||
|
|
||||||
if du.url_exists(user_id):
|
if du.url_exists(user_id):
|
||||||
if du.get_status(user_id):
|
if du.get_status(user_id):
|
||||||
wget.download(du.get_url(user_id), f'../data/icals/{str(user_id)}_new.ics')
|
wget.download(du.get_url(user_id), f'../data/icals/{str(user_id)}_new.ics')
|
||||||
|
|
||||||
os.remove(f'../data/icals/{str(user_id)}.ics')
|
os.remove(f'../data/icals/{str(user_id)}.ics')
|
||||||
os.rename(f'../data/icals/{str(user_id)}_new.ics', f'../data/icals/{str(user_id)}.ics')
|
os.rename(f'../data/icals/{str(user_id)}_new.ics', f'../data/icals/{str(user_id)}.ics')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
async def main():
|
||||||
loop = asyncio.get_event_loop()
|
# Создаем задачи для фоновых процессов
|
||||||
loop.create_task(alarm(60)) # ПРОВЕРКА КАЖДУЮ 1 МИНУТУ
|
asyncio.create_task(alarm(60)) # ПРОВЕРКА КАЖДУЮ 1 МИНУТУ
|
||||||
loop.create_task(update(780)) # ПРОВЕРКА КАЖДУЮ 13 МИНУТУ
|
asyncio.create_task(update(780)) # ПРОВЕРКА КАЖДУЮ 13 МИНУТУ
|
||||||
executor.start_polling(dp, skip_updates=True)
|
|
||||||
|
# Запускаем бота
|
||||||
|
await dp.start_polling(bot, skip_updates=True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
|
|
|
||||||
|
|
@ -1 +1,7 @@
|
||||||
TOKEN = "**********:***************************"
|
import os
|
||||||
|
|
||||||
|
# Получаем токен из переменной окружения
|
||||||
|
TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
|
||||||
|
|
||||||
|
if not TOKEN:
|
||||||
|
raise ValueError("TELEGRAM_BOT_TOKEN environment variable is required")
|
||||||
|
|
|
||||||
1406
data/icals/1.ics
1406
data/icals/1.ics
File diff suppressed because it is too large
Load diff
BIN
db/clock.db
BIN
db/clock.db
Binary file not shown.
BIN
db/users.db
BIN
db/users.db
Binary file not shown.
37
docker-compose.yml
Normal file
37
docker-compose.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
services:
|
||||||
|
calendarkin-bot:
|
||||||
|
build: .
|
||||||
|
container_name: y-calendarkin-bot
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
# Токен Telegram бота (обязательно)
|
||||||
|
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||||
|
|
||||||
|
# Дополнительные переменные окружения (опционально)
|
||||||
|
- TZ=Europe/Moscow
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Монтируем директорию с базами данных для сохранения данных
|
||||||
|
- ./db:/app/db
|
||||||
|
# Монтируем директорию с календарями
|
||||||
|
- ./data:/app/data
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
# Ограничения ресурсов
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
|
||||||
|
# Создаем именованные тома для данных (опционально)
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
|
ical_data:
|
||||||
7
env.example
Normal file
7
env.example
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Telegram Bot Token
|
||||||
|
# Получите токен у @BotFather в Telegram
|
||||||
|
TELEGRAM_BOT_TOKEN=your_bot_token_here
|
||||||
|
|
||||||
|
# Часовой пояс (опционально)
|
||||||
|
TZ=Europe/Moscow
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue