mirror of
https://github.com/EDeev/mospoly-helper.git
synced 2026-10-08 04:59:31 +03:00
Compare commits
No commits in common. "6d077ab95055435a862944303b66339a6bc33f6c" and "0d3e45c728ca791d87faa9ff62fecac46cd88f0b" have entirely different histories.
6d077ab950
...
0d3e45c728
7 changed files with 329 additions and 173 deletions
32
.github/workflows/ci.yml
vendored
32
.github/workflows/ci.yml
vendored
|
|
@ -1,32 +0,0 @@
|
||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
lfs: false
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.10"
|
|
||||||
- run: pip install ruff==0.14.0 -r requirements.txt
|
|
||||||
- name: Ruff (синтаксис и ошибки)
|
|
||||||
run: ruff check --select E9,F --ignore F403,F405 src/code
|
|
||||||
- name: Разбор номеров кабинетов и импорт бота
|
|
||||||
working-directory: src/code
|
|
||||||
env:
|
|
||||||
BOT_TOKEN: "123456:TEST"
|
|
||||||
run: |
|
|
||||||
python -c "
|
|
||||||
import asyncio, scripts
|
|
||||||
r = asyncio.run(scripts.get_routes('pr', 'пр2202а'))
|
|
||||||
assert r[-1].endswith('pr-2b-02f-2202аc.mp4'), r
|
|
||||||
assert asyncio.run(scripts.get_routes('pr', 'пр')) is None
|
|
||||||
import handlers, init
|
|
||||||
print('ok')"
|
|
||||||
57
README.en.md
57
README.en.md
|
|
@ -1,57 +0,0 @@
|
||||||
# MosPolyHelper
|
|
||||||
|
|
||||||
[Русский](README.md) · **English**
|
|
||||||
|
|
||||||
[](https://github.com/EDeev/mospoly-helper/actions/workflows/ci.yml)
|
|
||||||
|
|
||||||
A Telegram bot that sends a video route to a classroom in Moscow Polytechnic University buildings: pick a
|
|
||||||
campus, type the room number and get a video note with the way from the campus gate or from the building
|
|
||||||
entrance.
|
|
||||||
|
|
||||||
**Status:** team coursework (project practice, Moscow Polytech, group 241-327, 2025), completed · bot
|
|
||||||
[@MosPoly_Helperbot](https://t.me/MosPoly_Helperbot)
|
|
||||||
|
|
||||||
**Stack:** Python 3.10 · aiogram 3 · MoviePy 2 · FFmpeg · Docker
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Five campuses: Bolshaya Semyonovskaya, Pavla Korchagina, Pryanishnikova, Mikhalkovskaya, Avtozavodskaya
|
|
||||||
- A route from the campus gate or the building entrance: the clip is assembled from "building → floor →
|
|
||||||
room" fragments, sped up 2× and encoded for Telegram (H.264, 1500k)
|
|
||||||
- Finished clips are cached and repeat requests are instant; `combine.py` pre-builds the cache
|
|
||||||
- Room number validation with hints when the format or campus doesn't match
|
|
||||||
|
|
||||||
Videos were filmed for the Pryanishnikova buildings (76 rooms) and partly for Avtozavodskaya; for other
|
|
||||||
campuses the bot replies that the route isn't available yet and suggests writing by email.
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/EDeev/mospoly-helper.git && cd mospoly-helper
|
|
||||||
cp .env.example .env # BOT_TOKEN from @BotFather
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
Without Docker: Python 3.10+, FFmpeg, `pip install -r requirements.txt`, then
|
|
||||||
`cd src/code && BOT_TOKEN=… python bot.py`. More in [QUICKSTART.md](QUICKSTART.md) and [DOCKER.md](DOCKER.md) (in Russian).
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> The repository holds about 220 video clips (sources and cache), so it is over 500 MB.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Coursework (project practice, Moscow Polytechnic University, 2025). The code is open for study; there is
|
|
||||||
no separate license.
|
|
||||||
|
|
||||||
## Authors
|
|
||||||
|
|
||||||
- **Egor Deev** — [GitHub](https://github.com/EDeev)
|
|
||||||
- **Petr Saprykin** — [GitHub](https://github.com/PetrSaprykin)
|
|
||||||
- **Ruslan Starkov** — [GitHub](https://github.com/RayStar-k)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<sub>⭐ If you find this project useful, give it a star on GitHub!</sub>
|
|
||||||
<p><sub>Made with ❤️ by Moscow Polytech students, group 241-327</sub></p>
|
|
||||||
</div>
|
|
||||||
350
README.md
350
README.md
|
|
@ -1,76 +1,330 @@
|
||||||
# МосПолиХелпер
|
# МосПолиХелпер 🎓
|
||||||
|
|
||||||
**Русский** · [English](README.en.md)
|
**Телеграм-бот для навигации по кампусам Московского Политехнического Университета**
|
||||||
|
|
||||||
[](https://github.com/EDeev/mospoly-helper/actions/workflows/ci.yml)
|
[](https://python.org)
|
||||||
|
[](https://aiogram.dev/)
|
||||||
|
[](https://moviepy.readthedocs.io/)
|
||||||
|
[](https://www.docker.com/)
|
||||||
|
[](https://t.me/MosPoly_Helperbot)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
Telegram-бот, который присылает видео-маршрут до нужной аудитории в корпусах Московского Политеха:
|
## 📋 Описание
|
||||||
выбираешь кампус, пишешь номер кабинета — получаешь кружок с дорогой от входа на территорию или от входа
|
|
||||||
в корпус.
|
|
||||||
|
|
||||||
**Статус:** учебный командный проект (проектная практика, Московский Политех, группа 241-327, 2025),
|
МосПолиХелпер — это практическое решение реальной проблемы ориентирования в кампусах Московского Политехнического Университета. Бот разработан студентами для студентов и предоставляет интерактивные видео-маршруты до любого кабинета в пяти основных кампусах университета.
|
||||||
завершён · бот [@MosPoly_Helperbot](https://t.me/MosPoly_Helperbot)
|
|
||||||
|
|
||||||
**Стек:** Python 3.10 · aiogram 3 · MoviePy 2 · FFmpeg · Docker
|
Проект создан в рамках проектной практики и демонстрирует применение современных технологий для решения повседневных задач студенческой жизни.
|
||||||
|
|
||||||
## Возможности
|
### ✨ Основные возможности
|
||||||
|
|
||||||
- Пять кампусов: Большая Семёновская, Павла Корчагина, Прянишникова, Михалковская, Автозаводская
|
- 🗺️ **Интерактивная навигация** по 5 кампусам МосПолитеха
|
||||||
- Маршрут от входа на территорию или от входа в корпус: ролик собирается из фрагментов «корпус → этаж →
|
- 🎥 **Динамические видео-маршруты** от входа на территорию или от входа в корпус
|
||||||
кабинет», ускоряется вдвое и кодируется под Telegram (H.264, 1500k)
|
- ⚡ **Мгновенная отдача** готовых маршрутов благодаря интеллектуальному кешированию
|
||||||
- Готовые ролики кешируются, повторный запрос отдаётся сразу; `combine.py` собирает кеш заранее
|
- 📱 **Интуитивный интерфейс** с адаптивными inline-кнопками
|
||||||
- Проверка номера кабинета и подсказки, если формат не тот или кампус не совпадает
|
- 🔄 **Автоматическая обработка видео** с оптимизацией для Telegram (H.264, битрейт 1500k)
|
||||||
|
- 🚀 **Высокая производительность** — время отклика менее 5 секунд для кешированных маршрутов
|
||||||
|
- 📊 **Умная валидация** входных данных с подсказками пользователю
|
||||||
|
|
||||||
Видео сняты для корпусов на Прянишникова (76 кабинетов) и частично на Автозаводской; для остальных
|
### 🏢 Поддерживаемые кампусы
|
||||||
кампусов бот отвечает, что маршрута пока нет, и предлагает написать на почту.
|
|
||||||
|
|
||||||
| Кампус | Формат кабинета | Пример |
|
| Кампус | Код | Формат номера кабинета | Пример |
|
||||||
|---|---|---|
|
|--------|-----|------------------------|---------|
|
||||||
| Прянишникова | пр[корпус][этаж][номер] | `пр2202а` |
|
| **Большая Семёновская** | `bs` | [корпус][этаж]-[номер] | `а1-01`, `б2-15` |
|
||||||
| Автозаводская | ав[корпус][этаж][номер] | `ав2301` |
|
| **Павла Корчагина** | `pk` | пк[корпус][этаж][номер] | `пк1201`, `пк2315` |
|
||||||
| Павла Корчагина | пк[корпус][этаж][номер] | `пк1201` |
|
| **Прянишникова** | `pr` | пр[корпус][этаж][номер] | `пр1101`, `пр2208` |
|
||||||
| Михалковская | м[корпус][этаж][номер] | `м1205` |
|
| **Михалковская** | `mi` | м[корпус][этаж][номер] | `м1205`, `м3110` |
|
||||||
| Большая Семёновская | [корпус][этаж]-[номер] | `а1-01` |
|
| **Автозаводская** | `av` | ав[корпус][этаж][номер] | `ав2301`, `ав1105` |
|
||||||
|
|
||||||
## Запуск
|
> 📝 **Важно**: Указывайте номер кабинета точно в том формате, как он указан в личном кабинете или расписании
|
||||||
|
|
||||||
|
## 🚀 Быстрый старт
|
||||||
|
|
||||||
|
### 🐳 Запуск через Docker (рекомендуется)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/EDeev/mospoly-helper.git && cd mospoly-helper
|
# 1. Клонирование репозитория
|
||||||
cp .env.example .env # BOT_TOKEN от @BotFather
|
git clone https://github.com/EDeev/mospoly-helper.git
|
||||||
docker compose up -d
|
cd mospoly-helper
|
||||||
|
|
||||||
|
# 2. Настройка токена
|
||||||
|
cp .env.example .env
|
||||||
|
# Отредактируйте .env и добавьте ваш BOT_TOKEN
|
||||||
|
|
||||||
|
# 3. Запуск
|
||||||
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Без Docker: Python 3.10+, FFmpeg, `pip install -r requirements.txt`, затем
|
📖 **Подробнее**: [QUICKSTART.md](QUICKSTART.md) | [DOCKER.md](DOCKER.md)
|
||||||
`cd src/code && BOT_TOKEN=… python bot.py`. Подробнее — [QUICKSTART.md](QUICKSTART.md) и [DOCKER.md](DOCKER.md).
|
|
||||||
|
|
||||||
> [!NOTE]
|
### Команды бота
|
||||||
> В репозитории около 220 роликов (исходники и кеш), поэтому он весит больше 500 МБ.
|
|
||||||
|
|
||||||
## Структура
|
- `/start` — Приветствие и инструкции
|
||||||
|
- `/help` — Справка по командам
|
||||||
|
- `/route` — Построить маршрут до кабинета
|
||||||
|
|
||||||
|
### Использование
|
||||||
|
|
||||||
|
1. Запустите бота командой `/start`
|
||||||
|
2. Выберите команду `/route`
|
||||||
|
3. Укажите кампус из предложенного списка
|
||||||
|
4. Введите номер кабинета в точном формате
|
||||||
|
5. Выберите тип маршрута (от территории или от входа в корпус)
|
||||||
|
6. Получите видео с маршрутом
|
||||||
|
|
||||||
|
## 🛠️ Архитектура и технический стек
|
||||||
|
|
||||||
|
### 🧩 Технологии
|
||||||
|
|
||||||
|
| Компонент | Технология | Версия | Назначение |
|
||||||
|
|-----------|------------|---------|------------|
|
||||||
|
| **Backend** | Python | 3.10+ | Основной язык разработки |
|
||||||
|
| **Bot Framework** | Aiogram | 3.0.0 | Асинхронная работа с Telegram Bot API |
|
||||||
|
| **Video Processing** | MoviePy | 1.0.3 | Обработка и монтаж видеофрагментов |
|
||||||
|
| **Video Encoding** | FFmpeg | latest | Кодирование и оптимизация видео |
|
||||||
|
| **Data Storage** | JSON | native | Хранение пользовательских сессий |
|
||||||
|
| **State Management** | MemoryStorage | aiogram | Управление состояниями пользователей |
|
||||||
|
|
||||||
|
### 🏗️ Модульная архитектура
|
||||||
|
|
||||||
```
|
```
|
||||||
src/code/ бот: handlers.py (команды и кнопки), scripts.py (маршруты и монтаж), combine.py (кеш)
|
src/code/
|
||||||
src/videos/ исходные фрагменты: <кампус>/buildings, floors, offices
|
├── bot.py # 🚀 Точка входа и координация приложения
|
||||||
src/data/cache собранные маршруты
|
├── config.py # ⚙️ Конфигурационные параметры
|
||||||
site/ сайт проекта практики
|
├── database.py # 💾 Управление пользовательскими данными
|
||||||
reports/ отчёты участников и презентация
|
├── handlers.py # 🎛️ Обработчики команд и callback'ов
|
||||||
task/ задание практики
|
├── init.py # 🔧 Инициализация компонентов бота
|
||||||
|
├── scripts.py # 🎬 Обработка видео и формирование маршрутов
|
||||||
|
└── combine.py # 🔄 Утилита для предварительной генерации кеша
|
||||||
```
|
```
|
||||||
|
|
||||||
## Лицензия
|
**Принципы проектирования:**
|
||||||
|
- **Single Responsibility** — каждый модуль отвечает за конкретную задачу
|
||||||
|
- **Dependency Injection** — слабая связанность между компонентами
|
||||||
|
- **Async/Await** — асинхронная обработка для высокой производительности
|
||||||
|
- **Error Handling** — корректная обработка ошибок на всех уровнях
|
||||||
|
|
||||||
Учебный проект (проектная практика, Московский Политех, 2025). Код открыт для изучения, отдельной
|
## 📁 Структура данных и файловая система
|
||||||
лицензии нет.
|
|
||||||
|
|
||||||
## Авторы
|
```
|
||||||
|
📦 mospoly-helper/
|
||||||
|
├── 📂 src/code/ # Исходный код
|
||||||
|
├── 📂 data/ # Рабочие данные
|
||||||
|
│ ├── 📂 cache/ # Сгенерированные видеомаршруты
|
||||||
|
│ │ ├── 📄 *-all.mp4 # Полные маршруты (от территории)
|
||||||
|
│ │ └── 📄 *-small.mp4 # Короткие маршруты (от входа в корпус)
|
||||||
|
│ ├── 📂 users/ # JSON-файлы сессий пользователей
|
||||||
|
│ │ └── 📄 {user_id}.json # Данные конкретного пользователя
|
||||||
|
│ └── 📂 videos/ # Библиотека исходных видеофрагментов
|
||||||
|
│ ├── 📂 av/ # Автозаводская
|
||||||
|
│ │ ├── 📂 buildings/ # Видео от входа до корпуса
|
||||||
|
│ │ ├── 📂 floors/ # Видео по этажам
|
||||||
|
│ │ └── 📂 offices/ # Видео до конкретных кабинетов
|
||||||
|
│ ├── 📂 bs/ # Большая Семёновская
|
||||||
|
│ ├── 📂 mi/ # Михалковская
|
||||||
|
│ ├── 📂 pk/ # Павла Корчагина
|
||||||
|
│ └── 📂 pr/ # Прянишникова
|
||||||
|
└── 📂 logs/ # Журналы работы (рекомендуется)
|
||||||
|
```
|
||||||
|
|
||||||
- **Деев Егор** — [GitHub](https://github.com/EDeev)
|
## ⚡ Установка и настройка
|
||||||
- **Сапрыкин Пётр** — [GitHub](https://github.com/PetrSaprykin)
|
|
||||||
- **Старков Руслан** — [GitHub](https://github.com/RayStar-k)
|
### 📋 Системные требования
|
||||||
|
|
||||||
|
- **OS**: Linux (Ubuntu 20.04+), macOS, Windows 10+
|
||||||
|
- **Python**: 3.10 или выше
|
||||||
|
- **RAM**: минимум 2 ГБ (рекомендуется 4 ГБ)
|
||||||
|
- **Storage**: минимум 10 ГБ для видеофрагментов и кеша
|
||||||
|
- **Network**: стабильное интернет-соединение
|
||||||
|
|
||||||
|
### 🔧 Установка зависимостей
|
||||||
|
|
||||||
|
1. **Клонирование репозитория**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/EDeev/mospoly-helper.git
|
||||||
|
cd mospoly-helper
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Установка Python-зависимостей**
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Установка FFmpeg**
|
||||||
|
|
||||||
|
**Ubuntu/Debian:**
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install ffmpeg
|
||||||
|
```
|
||||||
|
|
||||||
|
**macOS (с Homebrew):**
|
||||||
|
```bash
|
||||||
|
brew install ffmpeg
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
Скачайте с [официального сайта](https://ffmpeg.org/download.html) и добавьте в PATH
|
||||||
|
|
||||||
|
### ⚙️ Конфигурация
|
||||||
|
|
||||||
|
1. **Создание Telegram бота**
|
||||||
|
- Перейдите к [@BotFather](https://t.me/BotFather)
|
||||||
|
- Создайте нового бота командой `/newbot`
|
||||||
|
- Сохраните полученный токен
|
||||||
|
|
||||||
|
2. **Настройка конфигурации**
|
||||||
|
```python
|
||||||
|
# config.py
|
||||||
|
BOT_TOKEN = "YOUR_BOT_TOKEN_HERE" # Замените на ваш токен
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Создание структуры каталогов**
|
||||||
|
```bash
|
||||||
|
mkdir -p data/{cache,users,videos/{av,bs,mi,pk,pr}/{buildings,floors,offices}}
|
||||||
|
mkdir -p logs
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🚀 Запуск
|
||||||
|
|
||||||
|
**Основной режим:**
|
||||||
|
```bash
|
||||||
|
cd src/code
|
||||||
|
python bot.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**С логированием:**
|
||||||
|
```bash
|
||||||
|
python bot.py 2>&1 | tee ../../logs/bot.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**Предварительная генерация кеша (опционально):**
|
||||||
|
```bash
|
||||||
|
python combine.py # Генерирует кеш для всех доступных маршрутов
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Алгоритмы и оптимизации
|
||||||
|
|
||||||
|
### 🎬 Обработка видео
|
||||||
|
|
||||||
|
Система обработки видео использует продвинутые техники оптимизации:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Параметры кодирования (scripts.py)
|
||||||
|
full_clip.write_videofile(
|
||||||
|
filename,
|
||||||
|
fps=30, # Оптимальная частота кадров
|
||||||
|
codec="libx264", # Эффективный кодек H.264
|
||||||
|
bitrate="1500k", # Баланс качество/размер
|
||||||
|
preset="fast", # Быстрое кодирование
|
||||||
|
ffmpeg_params=["-crf", "23"] # Constant Rate Factor для качества
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Применяемые оптимизации:**
|
||||||
|
- ✂️ **Удаление аудиодорожки** — экономия 30-40% размера файла
|
||||||
|
- ⏩ **Ускорение в 2 раза** — сокращение времени просмотра
|
||||||
|
- 📐 **Изменение разрешения до 400px** — оптимизация для мобильных устройств
|
||||||
|
- 🗜️ **CRF 23** — оптимальный баланс качества и размера файла
|
||||||
|
|
||||||
|
### ⚡ Система кеширования
|
||||||
|
|
||||||
|
**Двухуровневое кеширование:**
|
||||||
|
1. **Полные маршруты** (`*-all.mp4`) — от входа на территорию
|
||||||
|
2. **Короткие маршруты** (`*-small.mp4`) — от входа в корпус
|
||||||
|
|
||||||
|
**Алгоритм проверки кеша:**
|
||||||
|
```python
|
||||||
|
# Проверка существования готового файла
|
||||||
|
cache_path = f"../data/cache/{route_id}-{'all' if full_route else 'small'}.mp4"
|
||||||
|
if os.path.exists(cache_path):
|
||||||
|
return cache_path # Мгновенная отдача
|
||||||
|
else:
|
||||||
|
return await make_full_clip(video_segments) # Генерация нового
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📊 Производительность
|
||||||
|
|
||||||
|
| Метрика | Кешированный маршрут | Новый маршрут |
|
||||||
|
|---------|---------------------|---------------|
|
||||||
|
| **Время отклика** | < 3 секунды | 15-30 секунд |
|
||||||
|
| **Размер файла** | 2-8 МБ | аналогично |
|
||||||
|
| **Качество видео** | 400p, 30fps | аналогично |
|
||||||
|
| **Нагрузка на CPU** | минимальная | высокая |
|
||||||
|
|
||||||
|
## 🧪 Оптимизации производительности
|
||||||
|
|
||||||
|
| Компонент | До оптимизации | После оптимизации | Улучшение |
|
||||||
|
|-----------|----------------|-------------------|-----------|
|
||||||
|
| **Размер видео** | 15-25 МБ | 2-8 МБ | 🔻 70% |
|
||||||
|
| **Время генерации** | 45-60 сек | 15-30 сек | 🔻 50% |
|
||||||
|
| **Время отдачи (кеш)** | N/A | < 3 сек | ⚡ мгновенно |
|
||||||
|
| **Использование RAM** | 200-300 МБ | 100-150 МБ | 🔻 40% |
|
||||||
|
|
||||||
|
## 📦 combine.py — Пакетная генерация кеша
|
||||||
|
|
||||||
|
Утилита для предварительной генерации видеомаршрутов:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def combine_all_videos():
|
||||||
|
"""
|
||||||
|
Массовая генерация кеша для всех доступных маршрутов
|
||||||
|
|
||||||
|
Обрабатывает:
|
||||||
|
- Все корпуса: pr, pk, mi, av, bs
|
||||||
|
- Полные и короткие маршруты для каждого кабинета
|
||||||
|
- Пропускает несуществующие видеофрагменты
|
||||||
|
"""
|
||||||
|
corpora = ["pr", "pk", "mi", "av", "bs"]
|
||||||
|
|
||||||
|
for corpus in corpora:
|
||||||
|
offices_path = f"../videos/{corpus}/offices"
|
||||||
|
for video_file in os.listdir(offices_path):
|
||||||
|
# Генерация полного маршрута
|
||||||
|
await make_full_clip([building_path, floor_path, office_path])
|
||||||
|
# Генерация короткого маршрута
|
||||||
|
await make_full_clip([floor_path, office_path])
|
||||||
|
```
|
||||||
|
|
||||||
|
**Использование:**
|
||||||
|
```bash
|
||||||
|
cd src/code
|
||||||
|
python combine.py # Запуск в фоновом режиме для больших объемов
|
||||||
|
```
|
||||||
|
|
||||||
|
## 👨💻 Команда разработки
|
||||||
|
|
||||||
|
Проект создан в рамках проектной практики студентами группы **241-327** направления "Системная и программная инженерия":
|
||||||
|
|
||||||
|
| Участник | Роль | Вклад | Контакты |
|
||||||
|
|----------|------|-------|----------|
|
||||||
|
| **[Деев Егор Викторович](https://github.com/EDeev)** | Tech Lead & Backend Developer | Архитектура системы, обработка видео, оптимизация производительности | [@EDeev](https://t.me/EDeev) |
|
||||||
|
| **[Сапрыкин Пётр Иванович](https://github.com/PetrSaprykin)** | Frontend & UX Developer | Пользовательский интерфейс, логика взаимодействия, тестирование | [@PetrSaprykin](https://github.com/PetrSaprykin) |
|
||||||
|
| **[Старков Руслан Владимирович](https://github.com/RayStar-k)** | DevOps & QA Engineer | Развертывание, мониторинг, обеспечение качества | [@RayStar-k](https://github.com/RayStar-k) |
|
||||||
|
|
||||||
|
**Научное руководство:**
|
||||||
|
- **Куратор практики**: Баринова Наталья Владимировна
|
||||||
|
- **Кураторы проектной деятельности**: Киреева Галина Ивановна, Будылина Евгения Александровна
|
||||||
|
|
||||||
|
## 📞 Поддержка и контакты
|
||||||
|
|
||||||
|
### 🆘 Получение помощи
|
||||||
|
|
||||||
|
**Официальные каналы поддержки:**
|
||||||
|
|
||||||
|
1. **📧 Email**: [support@new-devs.ru](mailto:support@new-devs.ru)
|
||||||
|
- Время ответа: 24-48 часов
|
||||||
|
- Для технических вопросов и предложений
|
||||||
|
|
||||||
|
2. **🐙 GitHub Issues**: [Создать issue](https://github.com/EDeev/mospoly-helper/issues)
|
||||||
|
- Для сообщений об ошибках и feature requests
|
||||||
|
- Публичное обсуждение и tracking
|
||||||
|
|
||||||
|
## 📄 Лицензия и правовая информация
|
||||||
|
|
||||||
|
Этот проект создан в учебных целях как работа по дисциплине "Проектной практики" в Московском Политехническом Университете, 2024-2025 учебный год.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<sub>⭐ Если проект оказался полезным, поставьте звёздочку на GitHub!</sub>
|
<sub>МосПолиХелпер — делаем навигацию в университете простой и понятной! 🎓✨</sub>
|
||||||
<p><sub>Сделано с ❤️ студентами Московского Политеха, группа 241-327</sub></p>
|
<p><sub>Создано с ❤️ студентами МосПолитеха для студенческого сообщества</sub></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,16 @@
|
||||||
aiogram==3.13.1
|
# Основные зависимости для МосПолиХелпер
|
||||||
moviepy==2.1.2
|
# Telegram Bot Framework
|
||||||
|
aiogram==3.0.0
|
||||||
|
|
||||||
# FFmpeg ставится отдельно (в Docker-образе — из apt):
|
# Обработка видео
|
||||||
# Ubuntu/Debian: sudo apt install ffmpeg; macOS: brew install ffmpeg; Windows: https://ffmpeg.org/download.html
|
moviepy==1.0.3
|
||||||
|
|
||||||
|
# Дополнительные зависимости (устанавливаются автоматически с moviepy)
|
||||||
|
# imageio>=2.5
|
||||||
|
# imageio-ffmpeg>=0.2.0
|
||||||
|
# numpy>=1.17.3
|
||||||
|
|
||||||
|
# Примечание: FFmpeg должен быть установлен в системе отдельно
|
||||||
|
# Ubuntu/Debian: sudo apt update && sudo apt install ffmpeg
|
||||||
|
# macOS: brew install ffmpeg
|
||||||
|
# Windows: https://ffmpeg.org/download.html
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
class JsonTools:
|
class JsonTools:
|
||||||
def __init__(self, user_id):
|
def __init__(self, user_id):
|
||||||
self.f_name = f"../data/users/{user_id}.json"
|
self.f_name = f"../data/users/{user_id}.json"
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
return os.path.exists(self.f_name)
|
|
||||||
|
|
||||||
def save_json(self, data):
|
def save_json(self, data):
|
||||||
with open(self.f_name, "w", encoding="utf8") as f:
|
with open(self.f_name, "w", encoding="utf8") as f:
|
||||||
return json.dump(data, f)
|
return json.dump(data, f)
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ async def help_handler(msg: Message) -> None:
|
||||||
|
|
||||||
@router.message(Command("route"))
|
@router.message(Command("route"))
|
||||||
async def route_handler(msg: Message) -> None:
|
async def route_handler(msg: Message) -> None:
|
||||||
buttons = [[InlineKeyboardButton(text="На Большой Семёновской", callback_data="edu:bs")],
|
buttons = [[InlineKeyboardButton(text=f"На Большой Семёновской", callback_data="edu:bs")],
|
||||||
[InlineKeyboardButton(text="На Павла Корчагина", callback_data="edu:pk")],
|
[InlineKeyboardButton(text=f"На Павла Корчагина", callback_data="edu:pk")],
|
||||||
[InlineKeyboardButton(text="На Прянишкова", callback_data="edu:pr"),
|
[InlineKeyboardButton(text=f"На Прянишкова", callback_data="edu:pr"),
|
||||||
InlineKeyboardButton(text="На Михалковской", callback_data="edu:mi")],
|
InlineKeyboardButton(text=f"На Михалковской", callback_data="edu:mi")],
|
||||||
[InlineKeyboardButton(text="На Автозаводской", callback_data="edu:av")]]
|
[InlineKeyboardButton(text=f"На Автозаводской", callback_data="edu:av")]]
|
||||||
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
|
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
|
||||||
|
|
||||||
await msg.answer(text="Выберите корпус на котором вы хотите проложить маршрут:", reply_markup=keyboard)
|
await msg.answer(text="Выберите корпус на котором вы хотите проложить маршрут:", reply_markup=keyboard)
|
||||||
|
|
@ -50,12 +50,9 @@ async def route_button(call: CallbackQuery) -> None:
|
||||||
|
|
||||||
@router.message(F.content_type == ContentType.TEXT)
|
@router.message(F.content_type == ContentType.TEXT)
|
||||||
async def add_cabinet(msg: Message) -> None:
|
async def add_cabinet(msg: Message) -> None:
|
||||||
cab = msg.text.lower().strip()
|
cab = msg.text.lower()
|
||||||
|
|
||||||
jsn = JsonTools(msg.from_user.id)
|
jsn = JsonTools(msg.from_user.id)
|
||||||
if cab.startswith("/") or not jsn.exists():
|
|
||||||
await msg.answer("Сначала выберите корпус командой <b>/route</b>, затем напишите номер кабинета.")
|
|
||||||
return
|
|
||||||
user_dict = jsn.read_json()
|
user_dict = jsn.read_json()
|
||||||
edu_keys = list(user_dict.keys())
|
edu_keys = list(user_dict.keys())
|
||||||
|
|
||||||
|
|
@ -68,14 +65,13 @@ async def add_cabinet(msg: Message) -> None:
|
||||||
user_dict[edu_keys[0]] = lst_routes
|
user_dict[edu_keys[0]] = lst_routes
|
||||||
jsn.save_json(user_dict)
|
jsn.save_json(user_dict)
|
||||||
|
|
||||||
buttons = [[InlineKeyboardButton(text="От входа на территорию", callback_data="route:build")],
|
buttons = [[InlineKeyboardButton(text=f"От входа на территорию", callback_data="route:build")],
|
||||||
[InlineKeyboardButton(text="От входа в корпус", callback_data="route:floor")]]
|
[InlineKeyboardButton(text=f"От входа в корпус", callback_data="route:floor")]]
|
||||||
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
|
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
|
||||||
|
|
||||||
await msg.answer("Кабинет успешно получен, обрабатываем путь! Откуда вам проложить маршрут:", reply_markup=keyboard)
|
await msg.answer("Кабинет успешно получен, обрабатываем путь! Откуда вам проложить маршрут:", reply_markup=keyboard)
|
||||||
else:
|
else:
|
||||||
await msg.answer("Не получилось разобрать номер кабинета. Напишите его в точности как в личном "
|
await msg.answer("Обрыв программы по неизвестной причине!")
|
||||||
"кабинете, например <b>пр2202а</b>.")
|
|
||||||
else:
|
else:
|
||||||
await msg.answer("Вы скинули не кабинет! Я же вижу)")
|
await msg.answer("Вы скинули не кабинет! Я же вижу)")
|
||||||
|
|
||||||
|
|
@ -85,13 +81,9 @@ async def var_button(call: CallbackQuery) -> None:
|
||||||
action = call.data.split(":")[1]
|
action = call.data.split(":")[1]
|
||||||
|
|
||||||
jsn = JsonTools(call.from_user.id)
|
jsn = JsonTools(call.from_user.id)
|
||||||
user_dict = jsn.read_json() if jsn.exists() else {}
|
user_dict = jsn.read_json()
|
||||||
edu_keys = list(user_dict.keys())
|
edu_keys = list(user_dict.keys())
|
||||||
lst_routes = user_dict[edu_keys[0]] if edu_keys else []
|
lst_routes = user_dict[edu_keys[0]]
|
||||||
if not lst_routes:
|
|
||||||
# кнопки от прошлого запроса: кабинет ещё не выбран заново
|
|
||||||
await call.message.answer("Маршрут устарел, начните заново: <b>/route</b>")
|
|
||||||
return
|
|
||||||
|
|
||||||
msg = await call.message.answer("1. Получение видео...")
|
msg = await call.message.answer("1. Получение видео...")
|
||||||
path = ""
|
path = ""
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,7 @@
|
||||||
from moviepy import VideoFileClip, concatenate_videoclips
|
from moviepy import VideoFileClip, concatenate_videoclips
|
||||||
import os
|
import os, asyncio
|
||||||
|
|
||||||
async def get_routes(id_building: str, id_cab: str, other=False):
|
async def get_routes(id_building: str, id_cab: str, other=False):
|
||||||
try:
|
|
||||||
return _build_routes(id_building, id_cab, other)
|
|
||||||
except IndexError:
|
|
||||||
# номер кабинета короче ожидаемого формата
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _build_routes(id_building: str, id_cab: str, other=False):
|
|
||||||
if other:
|
if other:
|
||||||
id_cab = id_cab.replace('-', "")
|
id_cab = id_cab.replace('-', "")
|
||||||
building=id_cab[0]
|
building=id_cab[0]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue