1
0
Fork 0
mirror of https://github.com/EDeev/npr_meteobot.git synced 2026-10-08 04:59:33 +03:00

Compare commits

..

5 commits

Author SHA1 Message Date
69830f156f README на русском и английском 2026-10-02 23:36:49 +00:00
646943c21e requirements.txt и CI (ruff, создание базы) 2026-10-02 23:36:49 +00:00
28a5170be0 Мелкие исправления по ruff: дублирующееся имя обработчика /weather, неиспользуемые переменные 2026-10-02 23:36:49 +00:00
f223570bab Токен из окружения, база создаётся сама
- токен бота — из BOT_TOKEN, а не из config.py;
- sub.db (с идентификатором подписчика) убран из git: SQLighter создаёт таблицы и
  служебные строки из schema.sql, если базы нет.
2026-10-02 23:36:49 +00:00
be4411e269 Переводы строк приведены к LF 2026-10-02 23:33:14 +00:00
12 changed files with 480 additions and 475 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
* text=auto eol=lf
*.db binary

21
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- run: pip install ruff==0.14.0
- name: Ruff (синтаксис и ошибки)
run: ruff check --select E9,F .
- name: База создаётся из schema.sql
run: |
python -c "import sql; db = sql.SQLighter('/tmp/t.db'); db.add_user('1'); assert db.user_exists('1')"

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
__pycache__/
*.py[cod]
.venv/
.env
*.db

41
README.en.md Normal file
View file

@ -0,0 +1,41 @@
# NPR MeteoBot
[Русский](README.md) · **English**
[![CI](https://github.com/EDeev/npr_meteobot/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/npr_meteobot/actions/workflows/ci.yml)
A Telegram bot for schoolchildren in the Norilsk industrial area: it sends class cancellations due to frost
and wind ("aktirovka") for the user's shift and district and shows the weather forecast.
**Status:** school project (2022), completed, archived
**Stack:** Python 3.10 · aiogram 2 · SQLite · Selenium + BeautifulSoup (city administration site, Gismeteo) · pymorphy2
- Subscription to cancellations by shift (1 or 2) and district: Norilsk, Talnakh, Oganer, Kayerkan
- Regular checks of the administration site and broadcasts to subscribers
- Weather forecast for the day and up to 10 days ahead (Gismeteo data)
## Running
```bash
pip install -r requirements.txt
BOT_TOKEN=token_from_BotFather python bot.py
```
The `sub.db` database is created on first run from `schema.sql`. Cancellations need Chrome: the
administration site is read with Selenium.
## License
School project (2022). The code is open for study; there is no separate license.
## Author
**Egor Deev** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
---
<div align="center">
<sub>⭐ If you find this project useful, give it a star on GitHub!</sub>
<p><sub>Made with ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
</div>

138
README.md
View file

@ -1,139 +1,41 @@
# NPR MeteoBot # NPR MeteoBot
Автоматизированная система мониторинга метеорологических условий и уведомлений об актировках учебных занятий для Норильского промышленного района (НПР). **Русский** · [English](README.en.md)
## 📋 Описание проекта [![CI](https://github.com/EDeev/npr_meteobot/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/npr_meteobot/actions/workflows/ci.yml)
NPR MeteoBot представляет собой интеллектуальную систему на базе Telegram-бота, предназначенную для: Telegram-бот для школьников Норильского промышленного района: присылает актировки (отмену занятий
- Мониторинга погодных условий в четырех районах НПР из-за мороза и ветра) для своей смены и района и показывает прогноз погоды.
- Автоматического отслеживания актировок учебных занятий
- Персонализированных уведомлений пользователей
- Предоставления детализированных метеорологических данных
### Основные возможности: **Статус:** школьный проект (2022), завершён, в архиве
- **Персонализация**: Настройка по смене обучения и району проживания
- **Автоматизация**: Регулярный мониторинг официальных источников
- **Уведомления**: Система подписок с гибкими настройками
- **Актуальность**: Интеграция с Gismeteo и официальным сайтом города
## 🏗️ Архитектура системы **Стек:** Python 3.10 · aiogram 2 · SQLite · Selenium + BeautifulSoup (сайт администрации, Gismeteo) · pymorphy2
### Компоненты: - Подписка на актировки по смене (1 или 2) и району: Норильск, Талнах, Оганер, Кайеркан
- `bot.py` - Основной модуль Telegram-бота с обработкой команд - Регулярная проверка сайта администрации и рассылка подписчикам
- `sql.py` - Модуль работы с базой данных SQLite - Прогноз погоды на день и на 10 дней вперёд (данные Gismeteo)
- `info.py` - Парсинг данных с внешних источников
- `config.py` - Конфигурационные параметры системы
### Структура базы данных: ## Запуск
- **users**: Профили пользователей с настройками
- **weather**: Кэш метеорологических данных
- **parts**: Данные об актировках по сменам
- **data**: Системная информация
## 🛠️ Технологический стек ```bash
### Backend:
- **Python 3.7+** - Основной язык разработки
- **aiogram 2.x** - Фреймворк для Telegram Bot API
- **SQLite** - Локальная база данных
- **asyncio** - Асинхронное программирование
### Парсинг данных:
- **Selenium WebDriver** - Автоматизация браузера
- **BeautifulSoup4** - HTML-парсинг
- **requests** - HTTP-запросы
### Обработка данных:
- **pymorphy2** - Морфологический анализ русского языка
## 🚀 Установка и настройка
### Предварительные требования:
```python
# Установка ChromeDriver для Selenium
# Ubuntu/Debian:
sudo apt-get install chromium-chromedriver
# Windows: загрузить с https://chromedriver.chromium.org/
```
### Установка зависимостей:
```python
pip install -r requirements.txt pip install -r requirements.txt
BOT_TOKEN=токен_от_BotFather python bot.py
``` ```
### Конфигурация: База `sub.db` создаётся при первом запуске из `schema.sql`. Для актировок нужен Chrome: сайт
1. Получите токен бота в [@BotFather](https://t.me/BotFather) администрации читается через Selenium.
2. Обновите `API_TOKEN` в `config.py`
3. Создайте структуру базы данных SQLite
### Запуск: ## Лицензия
```python
python bot.py
```
## 📊 Функциональные возможности Школьный проект (2022). Код открыт для изучения, отдельной лицензии нет.
### Команды бота: ## Автор
- `/start` - Инициализация и настройка профиля
- `/town` - Изменение города и смены обучения
- `/acta` - Просмотр текущих актировок
- `/weather` - Детализированный прогноз погоды
- `/storm` - Штормовые предупреждения
- `/letter` - Управление подпиской на уведомления
- `/notice` - Настройка типа уведомлений
### Автоматические процессы: **Деев Егор Викторович** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
- **timer()** - Мониторинг актировок (каждую минуту)
- **wind()** - Обновление погодных данных (каждые 20 минут)
- **clear()** - Очистка устаревших данных (каждые 5 минут)
## 🎯 Целевые районы
Система обслуживает четыре района НПР:
- **Центральный район** (Норильск)
- **Район Талнах**
- **Район Оганер**
- **Район Кайеркан**
## 🔧 Конфигурационные параметры
### Временные интервалы мониторинга:
- Прогноз погоды: 1, 4, 7, 10, 13, 16, 19, 22 часа
- Проверка актировок: ежеминутно
- Обновление погоды: каждые 20 минут
### Источники данных:
- **Актировки**: [norilsk-city.ru/meteo](https://www.norilsk-city.ru/meteo/)
- **Погода**: [gismeteo.ru](https://www.gismeteo.ru/)
## 📈 Масштабируемость
Система спроектирована для:
- Обработки 17,000+ активных пользователей
- Асинхронной обработки запросов
- Отказоустойчивой работы с внешними API
## ⚠️ Ограничения
- Зависимость от доступности внешних источников данных
- Требует настройки ChromeDriver для корректного парсинга
- Локальная SQLite база данных (не подходит для кластерного развертывания)
## 📝 Лицензия
Проект разработан для образовательных и некоммерческих целей.
## 👥 Автор
**Деев Егор Викторович** - Backend Developer
- GitHub: [@EDeev](https://github.com/EDeev)
- Email: egor@deev.space
- Telegram: [@Egor_Deev](https://t.me/Egor_Deev)
--- ---
<div align="center"> <div align="center">
<sub>Разработано с использованием современных технологий асинхронного программирования и автоматизации веб-парсинга.</sub> <sub>⭐ Если проект оказался полезным, поставьте звёздочку на GitHub!</sub>
<p><sub>Создано с ❤️ от вашего дорогого - deev.space ©</sub></p> <p><sub>Сделано с ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
</div> </div>

2
bot.py
View file

@ -157,7 +157,7 @@ async def act(message: types.Message):
@dp.message_handler(commands=['weather']) @dp.message_handler(commands=['weather'])
async def storm(message: types.Message): async def weather_command(message: types.Message):
text = message.text.split() text = message.text.split()
try: try:
day = int(text[1]) day = int(text[1])

View file

@ -1,5 +1,7 @@
API_TOKEN = 'token from BotFather' # ОСНОВНОЙ import os
# API_TOKEN = 'token from BotFather' # ТЕСТЫ
# Токен бота от @BotFather — из переменной окружения BOT_TOKEN
API_TOKEN = os.environ.get('BOT_TOKEN', '')
CITY = {'Центральный район': [1, 'Норильск'], 'район Талнах': [2, 'Талнах'], CITY = {'Центральный район': [1, 'Норильск'], 'район Талнах': [2, 'Талнах'],
'ж/о Оганер': [3, 'Оганер'], 'район Кайеркан': [4, 'Кайеркан']} 'ж/о Оганер': [3, 'Оганер'], 'район Кайеркан': [4, 'Кайеркан']}

View file

@ -22,7 +22,7 @@ def act():
acta[acta.index(lst)] = [config.CITY[lst[0]], int(lst[1].split("-")[1].split()[0])] acta[acta.index(lst)] = [config.CITY[lst[0]], int(lst[1].split("-")[1].split()[0])]
return sorted(acta), count return sorted(acta), count
except Exception as e: except Exception:
return [], "" return [], ""
@ -57,7 +57,7 @@ def weather(moment):
dire = soup.find_all("div", class_="direction") dire = soup.find_all("div", class_="direction")
prec = soup.find_all("div", class_="item-unit") prec = soup.find_all("div", class_="item-unit")
k, s, weather = 0, 0, {} weather = {}
for i in range(8): for i in range(8):
par = wind[i].get_text().split()[0].split('-') par = wind[i].get_text().split()[0].split('-')

6
requirements.txt Normal file
View file

@ -0,0 +1,6 @@
aiogram==2.25.2
pymorphy2==0.9.1
pymorphy2-dicts-ru==2.4.417127.4579844
selenium==4.15.2
beautifulsoup4==4.12.3
requests==2.31.0

15
schema.sql Normal file
View file

@ -0,0 +1,15 @@
-- Схема базы бота и служебные строки, которые он ожидает (подписчиков нет)
CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, id_user VARCHAR (255) NOT NULL,
rating INTEGER (4) NOT NULL DEFAULT (0), status BOOLEAN NOT NULL DEFAULT (False),
send BOOLEAN NOT NULL DEFAULT (True), part INTEGER (1) NOT NULL, city INTEGER (1) NOT NULL);
CREATE TABLE IF NOT EXISTS parts (part INTEGER PRIMARY KEY AUTOINCREMENT, nor VARCHAR (3), tal VARCHAR (3),
oga VARCHAR (3), kae VARCHAR (3));
CREATE TABLE IF NOT EXISTS weather (id STRING, "1" VARCHAR, "4" VARCHAR, "7" VARCHAR, "10" VARCHAR,
"13" VARCHAR, "16" VARCHAR, "19" VARCHAR, "22" VARCHAR);
CREATE TABLE IF NOT EXISTS data (id INTEGER PRIMARY KEY AUTOINCREMENT, date DATE NOT NULL, part INTEGER (2) NOT NULL,
weath STRING);
INSERT INTO parts (part, nor, tal, oga, kae) VALUES (1, '11', '11', '11', '11'), (2, '11', '11', '11', '11');
INSERT INTO weather (id) VALUES (' '), ('tomorrow'), ('3-day'), ('4-day'), ('5-day'), ('6-day'), ('7-day'),
('8-day'), ('9-day'), ('10-day');
INSERT INTO data (id, date, part) VALUES (1, '2022-02-24', 1);

11
sql.py
View file

@ -1,11 +1,22 @@
import os
import sqlite3 import sqlite3
SCHEMA = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'schema.sql')
class SQLighter: class SQLighter:
def __init__(self, database): def __init__(self, database):
"""Подключаемся к БД и сохраняем курсор соединения""" """Подключаемся к БД и сохраняем курсор соединения"""
self.connection = sqlite3.connect(database) self.connection = sqlite3.connect(database)
self.cursor = self.connection.cursor() self.cursor = self.connection.cursor()
self._create_if_empty()
def _create_if_empty(self):
"""Новая база: создаём таблицы и служебные строки из schema.sql"""
exists = self.cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'users'").fetchone()
if not exists:
with open(SCHEMA, encoding='utf-8') as f:
self.connection.executescript(f.read())
def user_exists(self, id_user): def user_exists(self, id_user):
"""Проверяем, есть ли уже пользователь в базе""" """Проверяем, есть ли уже пользователь в базе"""

BIN
sub.db

Binary file not shown.