Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
d416f82
feat(dialogs): Внедрение базового функционала пользователя с использо…
AuthFailed Sep 26, 2025
2423901
feat(schedule): Возможность перелистывания графика дежурных и руковод…
AuthFailed Sep 26, 2025
c627db2
fix(schedule): Исправление открытия текущего дня в графиках
AuthFailed Sep 26, 2025
2900cf1
feat(kpi): Меню показателей на aiogram-dialog для специалистов
AuthFailed Sep 29, 2025
e30389f
feat(docker): Открытие redis в мир
AuthFailed Sep 29, 2025
da050b6
feat(kpi): Меню показателей на aiogram-dialog для руководителей
AuthFailed Sep 29, 2025
281460d
feat(cmds): Подсказка команде /whois
AuthFailed Sep 29, 2025
ec88408
feat(shop): Магазин на aiogram-dialog для специалистов
AuthFailed Sep 29, 2025
6ed172c
feat(inventory): Инвентарь на aiogram-dialog для специалистов
AuthFailed Sep 29, 2025
6258480
refactor(states): Классификация состояний специалистов
AuthFailed Sep 29, 2025
c897dcb
refactor: Разделение игровых окон диалога на папки
AuthFailed Sep 29, 2025
37e30d3
feat(shop): Активация предметов через меню магазина
AuthFailed Sep 29, 2025
ca93302
feat(achievements): Меню достижений для специалистов
AuthFailed Sep 29, 2025
046a558
feat(achievements): Меню истории баланса
AuthFailed Sep 29, 2025
de5ddc1
refactor(menu): Удаление неактуальных блоков кода
AuthFailed Sep 29, 2025
84739f2
feat(search): Меню поиска для специалистов
AuthFailed Sep 29, 2025
d0ae67d
feat(search): Меню деталей сотрудника из поиска для специалистов
AuthFailed Sep 29, 2025
387b51a
refactor(schedule): Рефакторинг использования ScheduleHandlerService
AuthFailed Sep 30, 2025
85984f6
feat(schedule): Поддержка детального режима графика для специалистов
AuthFailed Sep 30, 2025
84b7c36
feat(search): Поиск по запросу для специалистов
AuthFailed Sep 30, 2025
44e1855
feat(search): Унифицирование окон поиска
AuthFailed Sep 30, 2025
29adb1d
feat(activation): Унифицированная активация предметов для МИП, ГОК и …
AuthFailed Sep 30, 2025
a0b9042
feat(activation): Унифицированные окна предметов и достижений для ГОК…
AuthFailed Sep 30, 2025
399edfc
fix(achievements): Исправлено отображение фильтра по должности для сп…
AuthFailed Sep 30, 2025
f553aa9
feat(game): Фильтры для игровых меню для ГОК и МИП
AuthFailed Sep 30, 2025
1837d15
style(game): Текст для игрового меню
AuthFailed Sep 30, 2025
8a27cfb
feat(schedule): Меню графиков для руководителей
AuthFailed Sep 30, 2025
bf5685b
fix: Возвращен обработчик старта для МИП
AuthFailed Sep 30, 2025
1a45d13
refactor(docstrings): Большой рефакторинг №1
AuthFailed Oct 3, 2025
db9888b
refactor: Большой рефакторинг №2
AuthFailed Oct 3, 2025
ce0d82c
refactor: Большой рефакторинг №2
AuthFailed Oct 3, 2025
7468ad5
fix: Исправлено переоткрытие сессий с базами данных
AuthFailed Oct 3, 2025
d57b04d
fix: Исправлен возврат к старым результатам поиска
AuthFailed Oct 3, 2025
c436fb9
feat(groups): Просмотр групповых команд
AuthFailed Oct 3, 2025
ed866bc
feat(broadcast): Универсальный диалог рассылок
AuthFailed Oct 4, 2025
bbb0710
feat(groups): Универсальный диалог групп
AuthFailed Oct 4, 2025
ca5692f
feat(groups): Универсальный диалог поиска
AuthFailed Oct 4, 2025
333cf16
refactor: Перенос стандартных значений фильтров в on_start общих диал…
AuthFailed Oct 4, 2025
0d53523
feat(game): Универсальное игровое меню
AuthFailed Oct 4, 2025
9a166a5
chore: Возврат части стандартных значений диалогов
AuthFailed Oct 4, 2025
aea48d2
feat(files): Универсальный диалог управления файлами
AuthFailed Oct 7, 2025
a70eb35
feat(files): Загруженные файлы и их история
AuthFailed Oct 7, 2025
40d28e6
refactor: Рефакторинг ruff
AuthFailed Oct 7, 2025
2c0415c
refactor: Удалено излишнее логирование
AuthFailed Oct 7, 2025
e8f2887
feat(files): Загрузка файлов
AuthFailed Oct 8, 2025
28f8e83
feat(search): Управление уровнями доступа через поиск
AuthFailed Oct 8, 2025
6c26acd
feat(search): Просмотр графиков и показателей сотрудников через поиск
AuthFailed Oct 8, 2025
515ca09
feat(search): Просмотр истории достижений и инвентаря сотрудников
AuthFailed Oct 8, 2025
bc631ed
feat(schedule): Поддержка нового формата файла старшинств для НЦК
AuthFailed Sep 30, 2025
f1a7fde
feat: Универсальный диалог графиков и показателей
AuthFailed Oct 8, 2025
935714f
fix(achievements): Исправлен список достижений для менеджеров
AuthFailed Oct 8, 2025
01c51fb
feat(mailing): Отправка уведомлений об активациях специалисту
AuthFailed Oct 8, 2025
91bdea3
refactor(states): Удалены неиспользуемые состояния
AuthFailed Oct 8, 2025
4fee585
feat(group): Просмотр рейтинга группы для руководителей
AuthFailed Oct 8, 2025
15a8f9f
refactor: Удалены лишние вызовы №1
AuthFailed Oct 9, 2025
7faeccd
feat(docker): Установка git в контейнер для доступа к stp_database
AuthFailed Oct 11, 2025
1f6a9b1
refactor: Удаление uv.lock
AuthFailed Oct 11, 2025
e7a8117
feat(database): Использование модуля stp_database
AuthFailed Oct 11, 2025
2e1aec8
feat(files): Возможность использования симлинков папки файлов
AuthFailed Oct 11, 2025
4031155
fix: Исправлен импорт модуля
AuthFailed Oct 11, 2025
aa727fc
fix: Исправлен импорт модуля
AuthFailed Oct 11, 2025
11020d2
feat(database): Поддержка stp_database 1.3.5
AuthFailed Oct 11, 2025
a893b98
fix(rating): Исправлен формат отображения рейтинга для оценки клиента
AuthFailed Oct 11, 2025
8fc5a35
feat(group): Участники группы для руководителей
AuthFailed Oct 11, 2025
deb0374
feat(group): График группы для руководителей
AuthFailed Oct 11, 2025
b495d96
feat(group): Игровое меню группы для руководителей
AuthFailed Oct 11, 2025
b91caf4
fix(api): Исправлена инициализация API
AuthFailed Oct 12, 2025
25b9366
feat(broadcasts): Поддержка рассылки по уровню доступа
AuthFailed Oct 12, 2025
5d4a650
feat(docker): Пребилд docker image
AuthFailed Oct 12, 2025
c5a230c
feat(search): Поддержка возврата в список результатов поиска
AuthFailed Oct 12, 2025
55e5997
fix(kpi): Исправлен диалог KPI
AuthFailed Oct 12, 2025
b2c4c20
chore(docker): Замена url репозитория
AuthFailed Oct 12, 2025
6edfc2f
refactor(docker): Оптимизация билда
AuthFailed Oct 12, 2025
e219d72
feat(docker): Поддержка директории uploads с загруженными файлами
AuthFailed Oct 12, 2025
8387a62
feat: Кнопка-ссылка на саппорт
AuthFailed Oct 12, 2025
67a7096
feat(docker): Обновление .dockerignore
AuthFailed Oct 12, 2025
27bf9c5
feat(workflow): Workflow для билда docker image
AuthFailed Oct 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
81 changes: 78 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,79 @@
Git
.git
.gitignore
.gitattributes

# CI
.codeclimate.yml
.travis.yml
.taskcluster.yml

# Docker
docker-compose.yml
Dockerfile
.docker
.dockerignore

# Byte-compiled / optimized / DLL files
**/__pycache__/
**/*.py[cod]

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

# Translations
*.mo
*.pot

# Virtual environment
.env
.venv/
venv/
.idea/
cache/
README.MD

# PyCharm
.idea

# Python mode for VIM
.ropeproject
**/.ropeproject

# Vim swap files
**/*.swp

# VS Code
.vscode/
61 changes: 61 additions & 0 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Docker CI/CD

on:
push:
branches:
- main
- dev
- feature/**
tags:
- 'v*.*.*' # Build semver tags like v1.2.3

env:
REGISTRY: ghcr.io
IMAGE_NAME: stp-team/stpsher

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# Tag by Git SHA
type=sha,format=short
# Tag by branch name (e.g., main, dev)
type=ref,event=branch
# Tag by semver if pushed with a version tag
type=semver,pattern={{version}}
# Always push "latest" from main branch
type=raw,value=latest,enable={{ eq(github.ref_name, 'main') }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ venv/
ENV/
env.bak/
venv.bak/
uv.lock

# Pyre type checker
.pyre/
Expand Down
30 changes: 18 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
FROM python:3.13-slim
FROM ghcr.io/astral-sh/uv:latest AS uv
FROM python:3.13-slim AS runtime

WORKDIR /usr/src/app/stpsher-bot
WORKDIR /app

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
# Системные зависимости
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
&& rm -rf /var/lib/apt/lists/*

# Copy project files first for better caching
COPY pyproject.toml uv.lock* /usr/src/app/stpsher-bot/
# Python зависимости
COPY --from=uv /uv /usr/local/bin/uv
COPY pyproject.toml uv.lock* ./
RUN uv sync --frozen --no-dev
COPY . .

# Install Python dependencies with uv (this will create .venv)
RUN uv sync --frozen
# Добавляем виртуальное окружение в PATH
ENV PATH="/app/.venv/bin:$PATH"

# Copy application code
COPY . /usr/src/app/stpsher-bot
# Запрет Python записывать файлы .pyc и использовать буферизацию stdout
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

# Set the PATH to include the virtual environment
ENV PATH="/usr/src/app/stpsher-bot/.venv/bin:$PATH"
CMD ["uv", "run", "python", "bot.py"]
47 changes: 29 additions & 18 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
BotCommandScopeAllGroupChats,
BotCommandScopeAllPrivateChats,
)
from aiogram_dialog import setup_dialogs
from stp_database import create_engine, create_session_pool

from infrastructure.database.setup import create_engine, create_session_pool
from tgbot.config import Config, load_config
from tgbot.dialogs.menus import common_dialogs_list, dialogs_list
from tgbot.handlers import routers_list
from tgbot.middlewares.ConfigMiddleware import ConfigMiddleware
from tgbot.middlewares.DatabaseMiddleware import DatabaseMiddleware
Expand All @@ -28,7 +30,7 @@


async def on_startup():
"""Функция запуска бота"""
"""Функция, активируемая при запуске основного процесса бота."""
pass


Expand All @@ -38,12 +40,16 @@ def register_middlewares(
bot: Bot,
main_session_pool=None,
kpi_session_pool=None,
):
"""
Alternative setup with more selective middleware application.
Use this if you want different middleware chains for different event types.
"""
) -> None:
"""Установка middleware для определенных ивентов.

Args:
dp: Диспетчер ивентов
config: Конфигурация
bot: Экземпляр бота
main_session_pool: Сессия с базой данных STP
kpi_session_pool: Сессия с базой данных KPI
"""
config_middleware = ConfigMiddleware(config)
database_middleware = DatabaseMiddleware(
config=config,
Expand All @@ -67,16 +73,14 @@ def register_middlewares(
dp.chat_member.outer_middleware(middleware)


def get_storage(config):
"""
Return storage based on the provided configuration.
def get_storage(config) -> RedisStorage | MemoryStorage:
"""Возвращает хранилище исходя из конфигурации.

Args:
config (Config): The configuration object.
config: Объект конфигурации

Returns:
Storage: The storage object based on the configuration.

Хранилище RedisStorage или MemoryStorage
"""
if config.tg_bot.use_redis:
return RedisStorage.from_url(
Expand All @@ -87,7 +91,8 @@ def get_storage(config):
return MemoryStorage()


async def main():
async def main() -> None:
"""Основная функция запуска бота."""
setup_logging()

storage = get_storage(bot_config)
Expand All @@ -99,7 +104,10 @@ async def main():

# Определение команд для приватных чатов
await bot.set_my_commands(
commands=[BotCommand(command="start", description="Главное меню")],
commands=[
BotCommand(command="start", description="Главное меню"),
BotCommand(command="whois", description="Поиск сотрудников"),
],
scope=BotCommandScopeAllPrivateChats(),
)
await bot.set_my_commands(
Expand Down Expand Up @@ -145,22 +153,25 @@ async def main():

dp = Dispatcher(storage=storage)

# Create engines for different databases
# Создаем движки для доступа к базам
main_db_engine = create_engine(bot_config.db, db_name=bot_config.db.main_db)
kpi_db_engine = create_engine(bot_config.db, db_name=bot_config.db.kpi_db)

main_db = create_session_pool(main_db_engine)
kpi_db = create_session_pool(kpi_db_engine)

# Store session pools in dispatcher
# Храним сессии в диспетчере
dp["main_db"] = main_db
dp["kpi_db"] = kpi_db

dp.include_routers(*routers_list)
dp.include_routers(*dialogs_list)
dp.include_routers(*common_dialogs_list)
setup_dialogs(dp)

register_middlewares(dp, bot_config, bot, main_db, kpi_db)

# Setup all scheduled jobs using the new scheduler manager
# Запуск планировщика и добавление задач
scheduler_manager = SchedulerManager()
scheduler_manager.setup_jobs(main_db, bot, kpi_db)
scheduler_manager.start()
Expand Down
15 changes: 8 additions & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
services:
bot:
image: "stpsher-bot"
image: "ghcr.io/stp-team/stpsher:latest"
stop_signal: SIGINT
build:
context: .
working_dir: "/usr/src/app/stpsher-bot"
volumes:
- .:/usr/src/app/stpsher-bot
command: uv run bot.py
restart: always
env_file:
- ".env"
volumes:
- ./uploads:/app/uploads

logging:
driver: "json-file"
Expand All @@ -27,9 +23,14 @@ services:
command: redis-server --port $REDIS_PORT --save 20 1 --loglevel warning --requirepass $REDIS_PASSWORD
env_file:
- ".env"
ports:
- "6379:6379"
volumes:
- cache:/data

networks:
- stp_bots

volumes:
cache: { }

Expand Down
1 change: 1 addition & 0 deletions infrastructure/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Интеграция внешних API."""
1 change: 1 addition & 0 deletions infrastructure/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Интеграция API производственного календаря."""
12 changes: 4 additions & 8 deletions infrastructure/api/production_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ def __init__(self):
self._holidays_cache: Dict[int, Set[datetime.date]] = {}

async def get_holidays(self, year: int) -> Optional[Set[datetime.date]]:
"""
Получает список праздничных дней для указанного года
"""Получает список праздничных дней для указанного года

:param year: Год
:return: Множество праздничных дат или None при ошибке
Expand Down Expand Up @@ -48,8 +47,7 @@ async def get_holidays(self, year: int) -> Optional[Set[datetime.date]]:
return None

async def get_holiday_info(self, year: int) -> Optional[Dict[datetime.date, str]]:
"""
Получает информацию о праздниках с их названиями
"""Получает информацию о праздниках с их названиями

:param year: Год
:return: Словарь {дата: название праздника} или None при ошибке
Expand Down Expand Up @@ -82,8 +80,7 @@ async def get_holiday_info(self, year: int) -> Optional[Dict[datetime.date, str]
return None

async def is_holiday(self, date: datetime.date) -> bool:
"""
Проверяет, является ли указанная дата праздничной
"""Проверяет, является ли указанная дата праздничной

:param date: Дата для проверки
:return: True если праздник, False если нет
Expand All @@ -94,8 +91,7 @@ async def is_holiday(self, date: datetime.date) -> bool:
return date in holidays

async def get_holiday_name(self, date: datetime.date) -> Optional[str]:
"""
Получает название праздника для указанной даты
"""Получает название праздника для указанной даты

:param date: Дата
:return: Название праздника или None если не праздник
Expand Down
Empty file.
Loading
Loading