From 9123f579685c8e2b07550d2e996f27a923d19891 Mon Sep 17 00:00:00 2001 From: Egor Deev Date: Tue, 6 Oct 2026 01:10:52 +0000 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=B8,=20=D1=82=D0=B5=D1=81=D1=82=D1=8B,=20C?= =?UTF-8?q?I,=20Docker,=20=D0=BB=D0=B8=D1=86=D0=B5=D0=BD=D0=B7=D0=B8=D1=8F?= =?UTF-8?q?=20MIT=20=D0=B8=20README=20=D0=BD=D0=B0=20=D1=80=D1=83=D1=81?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D0=BC=20=D0=B8=20=D0=B0=D0=BD=D0=B3=D0=BB?= =?UTF-8?q?=D0=B8=D0=B9=D1=81=D0=BA=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit requirements.txt раньше не устанавливался (sqlite3, несуществующий enchant==1.6.6) и тянул неиспользуемые asyncio-mqtt, pyaudio, pydub. README: распознавание — SpeechRecognition, а не gTTS; число пользователей — по базе бота. --- .dockerignore | 5 + .env.example | 2 + .github/workflows/ci.yml | 19 ++++ .github/workflows/docker.yml | 42 ++++++++ .gitignore | 5 + Dockerfile | 23 ++++ LICENSE | 21 ++++ README.en.md | 103 ++++++++++++++++++ README.md | 199 +++++++++++++---------------------- compose.yaml | 11 ++ db/.gitkeep | 0 requirements-dev.txt | 3 + requirements.txt | 40 ++----- tests/conftest.py | 11 ++ tests/test_script.py | 65 ++++++++++++ 15 files changed, 390 insertions(+), 159 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.en.md create mode 100644 compose.yaml create mode 100644 db/.gitkeep create mode 100644 requirements-dev.txt create mode 100644 tests/conftest.py create mode 100644 tests/test_script.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..93d6b00 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +.env +db +tests +__pycache__ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7fcca50 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# Токен бота от @BotFather +BOT_TOKEN=123456:your-token diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7b77a23 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +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.12" + - run: sudo apt-get update -qq && sudo apt-get install -y -qq libenchant-2-2 hunspell-en-us + - run: pip install -r requirements-dev.txt + - run: ruff check --select E9,F,B code tests + - run: pytest -q diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..17433a2 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,42 @@ +name: Docker + +on: + push: + tags: ["v*"] + workflow_dispatch: + +jobs: + image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/login-action@v3 + with: + registry: dcr.deev.su + username: ${{ secrets.ZOT_USERNAME }} + password: ${{ secrets.ZOT_PASSWORD }} + - id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/edeev/chatping_abobot + dcr.deev.su/edeev/chatping_abobot + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + - uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..780d67e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +__pycache__/ +*.pyc +.pytest_cache/ +db/*.db diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4723f55 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +# словарь английского для распознавания текста в неправильной раскладке +RUN apt-get update \ + && apt-get install -y --no-install-recommends libenchant-2-2 hunspell-en-us \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY code/ code/ +COPY data/ data/ +RUN useradd --create-home --uid 1000 app && mkdir -p db && chown -R app:app /app +USER app +VOLUME ["/app/db"] + +# пути к базам и картинкам в коде — относительно папки code/ (../db, ../data) +WORKDIR /app/code +CMD ["python", "bot.py"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d424a8d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Egor Deev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..19ac620 --- /dev/null +++ b/README.en.md @@ -0,0 +1,103 @@ +# AboBot + +[Русский](README.md) · **English** + +[![CI](https://github.com/EDeev/chatping_abobot/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/chatping_abobot/actions/workflows/ci.yml) +[![Docker](https://github.com/EDeev/chatping_abobot/actions/workflows/docker.yml/badge.svg)](https://github.com/EDeev/chatping_abobot/actions/workflows/docker.yml) +[![License](https://img.shields.io/github/license/EDeev/chatping_abobot)](LICENSE) + +A Telegram bot for group chats. It: +- pings a member when their name comes up in a message or a voice note; +- keeps statistics for the chat and every member; +- fixes text typed in the wrong keyboard layout; +- transcribes and voices messages; +- runs playful events. + +The bot speaks Russian. + +**Status:** personal project, running since 2021 · bot [@chat_abobot](https://t.me/chat_abobot) · over +20,000 users from 45 chats in the bot's database (October 2026) + +**Stack:** Python 3.12 · aiogram 3 · pymorphy3 · pyenchant · SpeechRecognition · gTTS · SQLite · Docker + +## Features + +- **Name mentions.** Words of a message are reduced to their base form (pymorphy3) and compared with + members' names, so any grammatical case of a name mentions the right person. A custom name can be set with `/edit`. Names are also found + in voice notes up to a minute long. +- **`/all`** mentions every member. +- **All-time and monthly statistics** (`/stat_group`, `/stat_user`): messages, replies, commands, links, + media, stickers, voice and video notes. Monthly numbers reset at the start of each month. +- **Wrong keyboard layout.** A message like `ghbdtn` is repeated as «привет». English words are recognized + with a dictionary (pyenchant) and left alone. +- **Voice.** `/recognize` as a reply to a voice note transcribes it; «Озвучь - текст» turns text into a + voice note. +- **Events:** + - random numbers; + - reversing text; + - a language game; + - mock fights; + - picture reactions. + + Turned off with `/stop_bot`. +- Deleting service messages (joins, leaves, title and photo changes) when the bot is an admin. + +## Running + +```bash +git clone https://github.com/EDeev/chatping_abobot.git && cd chatping_abobot +cp .env.example .env # BOT_TOKEN from @BotFather +docker compose up -d +``` + +Prebuilt image: `docker pull ghcr.io/edeev/chatping_abobot` or `docker pull dcr.deev.su/edeev/chatping_abobot`. +The SQLite databases are created on first start. + +Without Docker you need Python 3.12, the enchant system library and an English dictionary +(`apt install libenchant-2-2 hunspell-en-us`). Then: `pip install -r requirements.txt` and +`cd code && BOT_TOKEN=… python bot.py`. + +## How it works + +``` +code/bot.py entry point and monthly statistics reset +code/handlers.py message, command, voice and event handlers +code/script.py statistics, name lookup, keyboard layout, text reversal +code/sql.py four SQLite databases: groups, users, all-time and monthly statistics +data/ event pictures and the greeting +``` + +Member statistics live in a separate table per chat — that is how the bot's production databases are +built. Counters are incremented in a single query, so simultaneous messages are not lost. Speech recognition +and synthesis (Google Web Speech and gTTS) run in a separate thread, so the bot never freezes. + +## Development + +```bash +pip install -r requirements-dev.txt +ruff check --select E9,F,B code tests && pytest +``` + +What the tests cover: +- statistics counters; +- mentions and `/all`; +- the monthly reset; +- keyboard layout, reversal and escaping; +- loading of the bot's handlers. + +The Docker image is built on `v*` tags and published to GitHub Packages and `dcr.deev.su`. + +## License + +MIT — see [LICENSE](LICENSE). + +## Author + +**Egor Deev** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space) + +--- + +
+ ⭐ If you find this project useful, give it a star on GitHub! +

Made with ❤️ — deev.space

+
diff --git a/README.md b/README.md index 63f87f0..819b65b 100644 --- a/README.md +++ b/README.md @@ -1,153 +1,100 @@ -# 🤖 ABOBOT - Telegram Group Management Bot +# AboBot -![Python](https://img.shields.io/badge/Python-3.8+-blue.svg) -![Aiogram](https://img.shields.io/badge/Aiogram-3.x-green.svg) -![SQLite](https://img.shields.io/badge/SQLite-3-orange.svg) -![License](https://img.shields.io/badge/License-MIT-yellow.svg) +**Русский** · [English](README.en.md) -Многофункциональный Telegram-бот для управления групповыми чатами с расширенной аналитикой и интерактивными возможностями. +[![CI](https://github.com/EDeev/chatping_abobot/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/chatping_abobot/actions/workflows/ci.yml) +[![Docker](https://github.com/EDeev/chatping_abobot/actions/workflows/docker.yml/badge.svg)](https://github.com/EDeev/chatping_abobot/actions/workflows/docker.yml) +[![License](https://img.shields.io/github/license/EDeev/chatping_abobot)](LICENSE) -## 🎯 Основные возможности +Telegram-бот для групповых чатов. Он: +- зовёт участника, когда его имя звучит в сообщении или в голосовом; +- ведёт статистику чата и каждого участника; +- исправляет текст, набранный в неправильной раскладке; +- распознаёт и озвучивает голосовые; +- устраивает шуточные ивенты. -### 📊 Система аналитики -- **Комплексная статистика** - детальный учёт активности участников -- **Временная сегментация** - анализ данных за месяц и весь период -- **Многомерные метрики** - сообщения, ответы, команды, медиафайлы, голосовые +**Статус:** личный проект, работает с 2021 года · бот [@chat_abobot](https://t.me/chat_abobot) · в базе +бота больше 20 000 пользователей из 45 чатов (октябрь 2026) -### 👥 Управление участниками -- **Интеллектуальные упоминания** - автоматическое обнаружение имён в сообщениях -- **Динамическое именование** - морфологическая обработка и нормализация -- **Групповые вызовы** - команда `/all` для оповещения всех участников +**Стек:** Python 3.12 · aiogram 3 · pymorphy3 · pyenchant · SpeechRecognition · gTTS · SQLite · Docker -### 🔧 Утилиты и инструменты -- **Транслитерация** - автоматический перевод с латиницы на кириллицу -- **Транскрипция** - конвертация голосовых сообщений в текст (gTTS) -- **Текстовые трансформации** - реверс слов и предложений -- **Генерация голоса** - озвучивание текстовых сообщений +## Возможности -### 🎮 Интерактивные функции -- **Игровые механики** - симуляция драк и взаимодействий -- **Рандомизация** - генерация случайных чисел в диапазоне -- **Советник** - интеграция с API для получения рандомных советов +- **Упоминания по имени.** Слова сообщения приводятся к начальной форме (pymorphy3) и сравниваются с + именами участников, поэтому участницу Машу позовут и «Маша», и «позови Машу», и «с Машей». Своё имя + для упоминаний можно задать командой `/edit`. Имена ищутся и в голосовых до минуты. +- **`/all`** — упомянуть всех участников. +- **Статистика за всё время и за месяц** (`/stat_group`, `/stat_user`): сообщения, ответы, команды, + ссылки, медиа, стикеры, голосовые и кружочки. Месячная обнуляется в начале месяца. +- **Неправильная раскладка.** Сообщение вида `ghbdtn` бот повторит как «привет». Английские слова он + отличает по словарю (pyenchant) и не трогает. +- **Голос.** `/recognize` ответом на голосовое — расшифровка; «Озвучь - текст» — голосовое из текста. +- **Ивенты:** + - «Число от 1 до 100»; + - «Переверни - …»; + - «Переведи - …» — на кирпичный язык; + - «Подраться с …»; + - «Чмокнуть» и другие действия с картинками. -## 🏗️ Архитектура системы + Отключаются командой `/stop_bot`. +- Удаление служебных сообщений (вход, выход, смена названия и фото) — если у бота права администратора. -### Технологический стек -``` -Backend Framework: aiogram (Async Telegram Bot API) -Database Engine: SQLite (многотабличная структура) -NLP Processing: pymorphy3 (морфологический анализ) -Speech Recognition: speech_recognition + gTTS -Async Framework: asyncio (асинхронная обработка) -``` +## Запуск -### Структура базы данных -``` -📁 Databases -├── base.db # Основная статистика групп -├── users.db # Маппинг пользователей -├── groups.db # Индивидуальная статистика участников -└── month.db # Временная сегментация данных -``` - -### Модульная организация -``` -📁 Project Structure -├── bot.py # Точка входа и инициализация -├── handlers.py # Обработчики событий и команд -├── script.py # Бизнес-логика и алгоритмы -├── sql.py # Абстракция базы данных -├── init.py # Конфигурация и зависимости -└── base.py # Константы и настройки -``` - -## 🚀 Быстрый старт - -### Установка зависимостей ```bash -pip install -r requirements.txt +git clone https://github.com/EDeev/chatping_abobot.git && cd chatping_abobot +cp .env.example .env # BOT_TOKEN от @BotFather +docker compose up -d ``` -### Конфигурация -1. Получите токен бота у [@BotFather](https://t.me/BotFather) -2. Обновите `TOKEN` в `code/base.py` -3. Настройте права администратора для автоудаления системных сообщений +Готовый образ: `docker pull ghcr.io/edeev/chatping_abobot` или `docker pull dcr.deev.su/edeev/chatping_abobot`. +Базы SQLite создаются при первом запуске. + +Без Docker нужны Python 3.12, системная библиотека enchant и английский словарь +(`apt install libenchant-2-2 hunspell-en-us`). Затем: `pip install -r requirements.txt` и +`cd code && BOT_TOKEN=… python bot.py`. + +## Как устроено + +``` +code/bot.py запуск и ежемесячное обнуление статистики +code/handlers.py обработчики сообщений, команд, голосовых и ивентов +code/script.py учёт статистики, поиск имён, раскладка, переворот текста +code/sql.py четыре базы SQLite: группы, пользователи, статистика за всё время и за месяц +data/ картинки ивентов и приветствие +``` + +Статистика участников хранится в отдельной таблице на каждый чат — так устроены рабочие базы бота. +Счётчики увеличиваются одним запросом, поэтому одновременные сообщения не теряются. Распознавание и синтез +речи (Google Web Speech и gTTS) идут в отдельном потоке, чтобы бот не замирал. + +## Разработка -### Запуск ```bash -cd code -python bot.py +pip install -r requirements-dev.txt +ruff check --select E9,F,B code tests && pytest ``` -## 📋 Команды +Что проверяют тесты: +- счётчики статистики; +- упоминания и `/all`; +- обнуление месячной статистики; +- раскладку, переворот и экранирование; +- что обработчики бота загружаются. -| Команда | Функционал | -|---------|------------| -| `/start`, `/help` | Интерактивное меню с описанием возможностей | -| `/all` | Упоминание всех участников группы | -| `/stat_group` | Детальная аналитика группы | -| `/stat_user` | Персональная статистика пользователя | -| `/recognize` | Транскрипция голосового сообщения | -| `/edit <имя>` | Установка кастомного имени для упоминаний | -| `/back_edit` | Возврат к динамическому именованию | -| `/start_bot` | Активация текстовых событий | -| `/stop_bot` | Деактивация текстовых событий | +Docker-образ собирается по тегу `v*` и публикуется в GitHub Packages и `dcr.deev.su`. -## 🎲 Текстовые события +## Лицензия -### Интерактивные команды -- **"Чмокнуть [имя]"** - позитивное взаимодействие -- **"Отмудохать [имя]"** - игровая агрессия -- **"Подраться с [имя]"** - симуляция боя с рандомным исходом -- **"Число от X до Y"** - генерация случайного числа -- **"Переведи (символ) - текст"** - трансформация в "кирпичный" язык -- **"Переверни - текст"** - реверс слов с сохранением пунктуации -- **"Озвучь - текст"** - генерация голосового сообщения +MIT — см. [LICENSE](LICENSE). -## 🔍 Технические особенности +## Автор -### Алгоритмы обработки -- **Морфологический анализ** - приведение имён к начальной форме -- **Детекция транслита** - эвристический алгоритм определения раскладки -- **Парсинг голоса** - интеграция Google Speech Recognition -- **Обработка пунктуации** - сохранение форматирования при трансформациях - -### Оптимизации производительности -- **Асинхронная архитектура** - неблокирующая обработка запросов -- **Кэширование соединений** - переиспользование подключений к БД -- **Ленивая загрузка** - подгрузка данных по требованию - -## 📈 Метрики и аналитика - -Система ведёт учёт следующих параметров: -- Общее количество сообщений -- Количество ответов на сообщения -- Использование команд бота -- Отправка медиафайлов и стикеров -- Голосовые сообщения и видеокружки -- Размещение ссылок - -## 🛠️ Требования к системе - -- Python 3.8+ -- Операционная система: Linux/Windows/macOS -- RAM: минимум 512MB -- Свободное место: 100MB для логов и медиафайлов - -## 📄 Лицензия - -Проект распространяется под лицензией MIT. - -## 👨‍💻 Автор - -**Деев Егор Викторович** - Backend Developer -- GitHub: [@EDeev](https://github.com/EDeev) -- Email: egor@deev.space -- Telegram: [@Egor_Deev](https://t.me/Egor_Deev) +**Деев Егор Викторович** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space) ---
- ⭐ Если проект оказался полезным, поставьте звезду на GitHub! -

Создано с ❤️ от вашего дорогого - deev.space ©

+ ⭐ Если проект оказался полезным, поставьте звёздочку на GitHub! +

Сделано с ❤️ — deev.space

diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..b5a1717 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,11 @@ +services: + bot: + build: . + image: ghcr.io/edeev/chatping_abobot:latest + env_file: .env + volumes: + - db:/app/db + restart: unless-stopped + +volumes: + db: diff --git a/db/.gitkeep b/db/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..b065da0 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +-r requirements.txt +pytest==8.4.2 +ruff==0.14.0 diff --git a/requirements.txt b/requirements.txt index 07c71ba..2ef520a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,34 +1,8 @@ -# Core Framework Dependencies -aiogram==3.10.0 -asyncio-mqtt==0.16.1 - -# Database Management -sqlite3 - -# Natural Language Processing -pymorphy3==1.2.1 +aiogram==3.23.0 +pymorphy3==2.0.6 pymorphy3-dicts-ru==2.4.417150.4580142 -enchant==1.6.6 - -# Speech Recognition & Synthesis -SpeechRecognition==3.10.4 -gTTS==2.5.1 -soundfile==0.12.1 - -# HTTP Client for API Integration -requests==2.31.0 - -# Audio Processing Dependencies -pyaudio==0.2.14 -pydub==0.25.1 - -# System Dependencies (if needed) -# Note: Some packages may require system-level installation -# Ubuntu/Debian: sudo apt-get install python3-enchant libenchant-2-2 -# macOS: brew install enchant portaudio -# Windows: Download pyaudio wheel from unofficial binaries - -# Development Dependencies (Optional) -# pytest==8.2.2 -# black==24.4.2 -# flake8==7.1.0 +pyenchant==3.2.2 +SpeechRecognition==3.14.4 +gTTS==2.5.4 +soundfile==0.13.1 +requests==2.32.5 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..982ccfd --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,11 @@ +import os +import sys + +# бот открывает базы по путям ../db относительно папки code — запускаем как в проде, но во временной папке +import tempfile + +ROOT = tempfile.mkdtemp() +os.makedirs(os.path.join(ROOT, "code")) +os.chdir(os.path.join(ROOT, "code")) +os.environ.setdefault("BOT_TOKEN", "123456:TEST") +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "code")) diff --git a/tests/test_script.py b/tests/test_script.py new file mode 100644 index 0000000..a0895cd --- /dev/null +++ b/tests/test_script.py @@ -0,0 +1,65 @@ +import script +from init import du, dg + + +def test_md_and_plain(): + assert script.md("ivan_petrov *x*") == "ivan\\_petrov \\*x\\*" + assert script.plain("[a]_b*") == "ab" + + +def test_join_names(): + assert script.join_names(["а"]) == "а" + assert script.join_names(["а", "б"]) == "а и б" + assert script.join_names(["а", "б", "в"]) == "а, б и в" + + +def test_translator_fixes_wrong_layout(): + assert script.translator(["ghbdtn", "vbh"]) == "привет мир" + + +def test_revers_and_lang_form(): + assert script.revers("Привет, мир!", True) == "Тевирп, рим!" + assert script.lang_form(["кот"]) == "когот" + + +def test_stats_counters_and_mentions(): + chat, author, other, third = -100500, 1, 2, 3 + script.upd_stat(author, chat, 1, "Автор") + script.upd_stat(other, chat, 1, "Иван_") + script.upd_stat(other, chat, 1, "Иван_") + script.upd_stat(third, chat, 2, "Пётр") + + group_id = du.get_group_id(chat) + ivan = du.get_user_id(other) + assert dg.stat_user(ivan, group_id)[0] == 2 # два сообщения — счётчик атомарный + + # упоминание: спецсимволы Markdown из имени внутри ссылки убираются, автор себя не упоминает + text = script.notice(["иван_", "автор"], False, group_id, author) + assert text == "[Иван](tg://user?id=2), тебя упомянули)" + assert script.notice(["автор"], False, group_id, author).startswith("[Автор]") + + every = script.notice(du.get_user_id(author), True, group_id, author) + assert "[Иван](tg://user?id=2) и [Пётр](tg://user?id=3) вас вызывает Автор" == every + + +def test_bot_imports(): + import bot # noqa: F401 — обработчики и роутер собираются без ошибок + + +def test_month_reset(): + from datetime import date + + import bot + from init import db, dm + + chat, user = -200300, 10 + script.upd_stat(user, chat, 1, "Маша") + group_id = du.get_group_id(chat) + member = du.get_user_id(user) + + assert bot.reset_month_if_needed(date(2026, 10, 6)) is False # первый запуск — только запомнить + assert dm.stat_user(member, group_id)[0] == 1 + assert bot.reset_month_if_needed(date(2026, 10, 20)) is False # тот же месяц + assert bot.reset_month_if_needed(date(2026, 11, 1)) is True + assert dm.stat_user(member, group_id)[0] == 0 and db.month_stat_group(group_id)[0] == 0 + assert dg.stat_user(member, group_id)[0] == 1 # статистика за всё время не трогается