Python 3.11–3.12, aiogram 3, SQLite. Сообщения клиента собираются в одно обращение. Если за RESPONSE_TIMEOUT секунд специалист не ответил в клиентском чате, бот создаёт заявку в рабочем чате. Есть роли, статистика, Excel-выгрузки, объявления, напоминания и автоочистка.
Поддерживаются Debian 12 и Ubuntu 24.04, Python 3.11–3.12, systemd. Нужны интернет, права sudo и интерактивный терминал. Для Windows ниже приведён ручной запуск; установщик службы предназначен для Linux.
Скачайте установщик, просмотрите его и запустите:
sudo apt-get update && sudo apt-get install -y curl
curl --fail --location --output install.sh https://raw.githubusercontent.com/IndeecDen/tg_ts_ticket_bot/main/install.sh
less install.sh
sudo bash install.sh --branch mainУстановщик скачивает проект из GitHub, устанавливает системные и Python-зависимости в отдельное окружение, создаёт пользователя ticketbot, службу tg_ts_ticket_bot и включает запуск при загрузке сервера. При установке он запросит:
BOT_TOKENиз BotFather; ввод скрыт.WORK_CHAT_ID— отрицательный ID рабочего группового чата специалистов.- Время ожидания ответа в секундах (по умолчанию 300).
Проверяется формат значений. Действительность токена и права в чате нужно проверить после запуска через Telegram и журнал службы. Токен не передаётся аргументом командной строки. Настройки доступны только пользователю службы и root. База создаётся автоматически при первом запуске: исходная база с заявками, роли и личные настройки в репозитории отсутствуют.
| Путь | Содержимое |
|---|---|
/opt/tg_ts_ticket_bot |
Исходники и .venv, принадлежат root |
/var/lib/tg_ts_ticket_bot/.env |
Токен и настройки, права 600 |
/var/lib/tg_ts_ticket_bot/bot.db |
База новой установки |
/var/log/tg_ts_ticket_bot/bot.log |
Журнал с ротацией |
/etc/systemd/system/tg_ts_ticket_bot.service |
Служба и автозапуск |
Дайджесты и автоочистка используют часовой пояс сервера (timedatectl). Таймеры рассылок используют отдельную настройку TIMEZONE (по умолчанию UTC). Код защищён от записи пользователем службы; база, настройки и журнал размещены отдельно.
sudo systemctl status tg_ts_ticket_bot
sudo journalctl -u tg_ts_ticket_bot -f
sudo systemctl restart tg_ts_ticket_bot
sudo systemctl stop tg_ts_ticket_bot
# Отключить автозапуск и остановить:
sudo systemctl disable --now tg_ts_ticket_botДля изменения токена или рабочего чата остановите службу, отредактируйте sudo nano /var/lib/tg_ts_ticket_bot/.env и запустите sudo systemctl start tg_ts_ticket_bot. Команда бота /set_timeout сохраняет таймаут в этот же файл.
Повторная установка поверх существующих каталогов, пользователя или службы запрещена. При ошибке установщик оставляет созданные файлы для диагностики и не удаляет данные. Исправьте причину по сообщению ошибки; не удаляйте существующую базу ради повторного запуска установщика.
Остановите службу и сделайте резервную копию всего /var/lib/tg_ts_ticket_bot в закрытом каталоге. Затем:
sudo systemctl stop tg_ts_ticket_bot
sudo git -C /opt/tg_ts_ticket_bot pull --ff-only
sudo /opt/tg_ts_ticket_bot/.venv/bin/python -m pip install -r /opt/tg_ts_ticket_bot/requirements.txt
sudo systemctl start tg_ts_ticket_bot
sudo systemctl status tg_ts_ticket_botОбновление исходников не затрагивает /var/lib/tg_ts_ticket_bot. Общие правила миграции и отката приведены ниже.
Создайте новое виртуальное окружение для своей ОС. Папка venv, перенесённая с Linux, не подходит для Windows.
Windows PowerShell:
py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Copy-Item .env.example .env # только при первой установке, если .env ещё нет
.\.venv\Scripts\python.exe main.pyLinux:
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
cp -n .env.example .env
.venv/bin/python main.pyВ .env укажите BOT_TOKEN из BotFather и WORK_CHAT_ID рабочего чата. Добавьте бота в рабочий и клиентские чаты. Чтобы получать обычные сообщения групп, настройте доступ бота к сообщениям группы; для проверки участников и очистки нужны соответствующие права администратора.
Параметры:
| Переменная | Назначение | По умолчанию |
|---|---|---|
BOT_TOKEN |
Токен Telegram-бота | Обязателен |
WORK_CHAT_ID |
Рабочий чат специалистов | Обязателен |
RESPONSE_TIMEOUT |
Ожидание ответа перед созданием заявки, секунды | 300 |
DATABASE_PATH |
Путь к SQLite | bot.db |
LOG_LEVEL |
Уровень журнала | INFO |
LOG_PATH |
Файл журнала | bot.log |
TIMEZONE |
Часовой пояс IANA для рассылок, например Europe/Moscow |
UTC |
BROADCAST_SEND_INTERVAL |
Пауза между отправками рассылок, секунды, от 0.05 до 60 | 0.05 |
BOT_ENV_FILE |
Путь к файлу настроек; задаётся в окружении процесса до запуска | .env в папке проекта |
Относительные пути базы и журнала считаются от папки проекта, а не текущей папки запуска. Файл настроек выбирается через BOT_ENV_FILE, по умолчанию .env читается из папки проекта. Переменные окружения процесса имеют приоритет над файлом. Если RESPONSE_TIMEOUT задан в окружении службы, обновляйте его там: /set_timeout сохраняет значение в выбранный файл настроек и памяти текущего процесса. Изменение применяется к новым обращениям.
Расписание напоминаний и исторические даты заявок используют локальное время сервера. Настройте его часовой пояс до запуска и не меняйте его при работе с существующей историей. Рассылки сохраняют время в UTC и показывают в явно указанном TIMEZONE; неверный пояс останавливает запуск с ошибкой конфигурации. Зависимость tzdata обеспечивает работу IANA-поясов в том числе в Windows.
Администраторы рабочего чата и зарегистрированные ботадмины могут управлять настройками, в том числе в личной переписке. Специалисты и ботадмины добавляются командами /add_spec <user_id> и /add_botadm <user_id>. Полный список — /help.
- Клиент: описание проблемы,
/cancel, кнопка отмены созданной заявки. - Специалист: взять заявку, завершить свою заявку,
/my_active_requests,/my_stats. - Администратор:
/stats,/export, настройки,/close_all. /assign_request <номер> <ID специалиста>— назначить исполнителя существующей заявки в работе, в том числе восстановить привязку старой заявки без ID. Только для администратора./cancelво время диалога настройки отменяет этот диалог; кнопка «Назад» также очищает состояние диалога.
Права повторно проверяются при нажатии кнопок статистики и действиях с заявками. Закрытие и личная статистика используют неизменяемый Telegram ID, а не отображаемое имя.
- Остановите старый процесс бота. Запускайте один экземпляр с одним токеном и одной базой.
- Сохраните
.envи базу. Не заменяйте их примерами из проекта. - Обновите исходники и установите зависимости в окружение нужной ОС.
- Запустите
main.py. Перед обновлением схемы до версии 2 бот автоматически создаст согласованную SQLite-копиюbot.db.backup-<дата-время>(имя зависит отDATABASE_PATH). Затем добавит таблицы очередей, рассылок и индексы без удаления заявок. - Проверьте журнал,
/open_requestsи доставку тестового обращения.
Старые незаконченные доставки активных заявок будут поставлены в очередь автоматически. Старые записи active_timers сохраняются с предупреждением в журнале: прежняя версия не записывала тексты ожидающих сообщений на диск, поэтому восстановить уже потерянные тексты невозможно. Новые ожидающие обращения сохраняются целиком в pending_requests.
Старые заявки без specialist_id не привязываются по совпадению имени. Для активных используйте /assign_request; архивные записи остаются в общей статистике с пометкой «архив без ID» и не включаются в личную статистику.
Для отката остановите бот и восстановите резервную копию вместе с предыдущими исходниками. Не подменяйте файл работающей SQLite. Для регулярных резервных копий работающей базы используйте SQLite Backup API; простое копирование только .db при активном WAL может пропустить последние изменения.
Откройте личный диалог → /start → 📣 Рассылки (также есть в «⚙️ Настройки» и по /broadcast). Доступ имеют ботадмины и администраторы рабочего чата; права проверяются при каждом действии и перед запуском таймера.
- Нажмите «✏ Новая рассылка» и пришлите одно сообщение: текст, фото, документ, видео или аудио. Подпись необязательна. Альбомы, анимации, стикеры, голосовые и другие типы отклоняются целиком.
- Выделяйте текст средствами Telegram: жирный, курсив, подчёркивание, зачёркивание, спойлер, ссылки, код, цитаты. Бот сохраняет
entities/caption_entitiesи UTF-16-смещения, включая эмодзи. Написанные вручную HTML-теги остаются обычным текстом. - Лимиты: 4096 символов текста, 1024 символа подписи после разбора сущностей. Смещения форматирования считаются отдельно в UTF-16. Превышение лимита отклоняется без обрезки. Составные эмодзи могут содержать несколько Unicode-символов.
- Бот показывает сохранённое сообщение и новое сообщение ниже него с тремя кнопками: «🚀 Отправить сейчас», «🕒 По таймеру», «✖ Отменить». До подтверждения сообщений в группы нет. Число известных групп предварительное: окончательные адресаты фиксируются при запуске.
- Таймер принимает
30(минуты от текущего момента, 1–525600) или2030-01-02 10:00. Используется показанныйTIMEZONE. Прошедшее, несуществующее и неоднозначное местное время отклоняется. При ошибке черновик и кнопка отмены сохраняются. - «📋 Список рассылок» → «📋 Открыть #…»: автор, статус, даты и пояс, счётчики, обновление и ошибки по группам с пагинацией. Через «Предпросмотр» можно снова открыть черновик после перезапуска или неудачного предпросмотра.
«✖ Отменить» и /cancel отменяют текущую подготовку, включая этап после предпросмотра. Запланированную рассылку можно отменить из списка до запуска. После запуска отмена уже доставленных сообщений не выполняется. Кнопка «Назад» закрывает диалог; сохранённый черновик доступен в списке. Действия создания, планирования, запуска и отмены записываются в broadcast_audit.
Постоянный реестр пополняется входящими сообщениями групп и my_chat_member; старые ID восстанавливаются из заявок и ожидающих обращений с проверкой типа чата, членства и права отправки. Личные чаты, каналы, рабочий чат из текущей конфигурации и группы с удалённым/ограниченным ботом исключаются. Права проверяются также перед доставкой; изменение прав уже после проверки может дать отдельную ошибку Telegram.
Telegram не выдаёт полный список чатов бота. Если старая группа ещё неизвестна, отправьте в ней /whoami@ИмяВашегоБота. Такая адресованная команда работает при privacy mode; выдавать права администратора только ради регистрации необязательно, но нужно разрешение отправлять сообщения. Пересылка в личный диалог исходную группу не регистрирует. После восстановления прав повторите /whoami либо обновите членство бота.
Миграция в супергруппу обрабатывается по обоим служебным полям сообщения и ответу API: сохраняется соответствие старого/нового ID, меняется адрес будущей доставки, исходный чат существующих заявок сохраняется отдельно. История результатов отправки не удаляется.
- Черновики, содержимое,
file_id, расписания, адресаты, результаты и задержки находятся в той же SQLite. Новые таблицыbroadcast_*используют существующийAsyncDatabase; очередь карточек заявокdelivery_queueостаётся специализированной для их ревизий. - Отправка выполняется фоновым работником. Создание уникальных заданий «рассылка — чат» и фиксация адресатов транзакционные. Повторное подтверждение не создаёт вторую рассылку. SQL выполняется вне event loop; сетевых запросов внутри транзакций нет.
- Сохранённые таймеры запускаются после восстановления, включая пропущенное время. Отзыв роли автора отменяет расписание; временная ошибка проверки роли откладывает запуск.
- Скорость рассылок — не более 20 отправок/с по умолчанию, в одну группу — не чаще раза в 3.1 с между любыми рассылками. 429 сохраняет общую паузу очереди рассылок и задержку задания. Другие функции бота также расходуют лимит Telegram: при совместной нагрузке возможны дополнительные 429.
- Ошибки одного адресата не останавливают остальных. Сетевой обрыв, 5xx и прерванная при остановке отправка получают «Неопределённый результат»: автоматически повторно не отправляются. Откройте «Ошибки», вручную проверьте группу и решите, нужна ли отдельная новая рассылка. Гарантии «ровно один раз» нет.
- Один процесс на одну базу: запуск защищён OS-блокировкой
<DATABASE_PATH>.lock, автоматически освобождаемой при остановке/аварии. Удалять файл блокировки во время работы нельзя. Для нескольких серверов/сетевой файловой системы этот режим не предназначен. - Поддерживается одно вложение и разовый таймер.
file_idпринадлежит текущему боту; смена токена на другого бота может сделать старые вложения недоступными. Telegram может ограничивать отдельные сущности (например, custom emoji); ошибка предпросмотра показывается явно. Платные рассылки и Stars не используются.
Лимиты методов сверены 08.10.2026 по актуальным описаниям aiogram и исходникам Telegram/TDLib. Для новой загрузки: фото до 10 МБ, документ/видео/аудио до 50 МБ; эта функция повторно отправляет уже загруженные файлы по file_id, не скачивает и не загружает их заново. Прямая страница core.telegram.org была недоступна из среды проверки. Предпросмотр и доставка используют один отправитель и одинаковые параметры, включая спойлер/расположение подписи.
- Текст, идентификаторы сообщений и медиа сохраняются до ожидания таймаута. Одновременно принимаются обращения разных пользователей одного чата.
- Создание заявки и заданий доставки выполняется одной транзакцией. Повторная обработка сохранённого обращения не создаёт вторую заявку.
- Рабочий чат и клиент уведомляются независимо. Сбой одного направления не отменяет сохранённую заявку.
- Очередь
delivery_queueхранит попытки, время следующей попытки и последнюю ошибку. Сетевые ошибки повторяются с увеличением задержки до часа;RetryAfterучитывается, недоступный чат повторяется через час. - Изменение статуса также ставит обновления обоих чатов в очередь. Ошибка редактирования не оставляет статус только в БД навсегда.
- SQLite работает вне основного цикла событий. Сетевые операции разных заявок выполняются с ограничением параллельности; общего замка на сетевые запросы нет.
- Описания полностью сохраняются в БД. Карточка показывает сокращённый текст и первое поддерживаемое вложение, как и прежняя версия; полное описание закрытых заявок доступно в
/export. Длинные отчёты разбиваются на сообщения с корректным HTML.
Telegram не предоставляет ключ идемпотентности для отправки: при обрыве соединения после приёма сообщения Telegram, но до получения ответа ботом повторная попытка может создать повторное уведомление. Сама заявка в БД при этом остаётся одна. Невозможно программно исправить блокировку бота пользователем, отсутствие прав или недоступность сети; очередь сохраняет уведомления для повторов.
main.py— запуск, зависимости, остановка фоновых задач.handlers/common.py— роли и общие состояния диалогов.handlers/commands.py— команды клиентов/специалистов и ввод дат.handlers/admin.py— администрирование и настройки.handlers/callbacks_requests.py— разрешённые переходы статусов.handlers/callbacks_stats.py,handlers/keyboards.py— отчёты и меню.services/requests.py— обработка сохранённых обращений и доставка.services/scheduler.py— дайджесты и автоочистка.handlers/broadcast.py,services/broadcasts.py,db/broadcast_store.py— диалоги, валидация, реестр, расписание и постоянная очередь рассылок.utils/process_lock.py— защита запуска второго процесса на той же базе.db/database.py,db/request_store.py— SQL, миграции и транзакции.db/async_database.py— выполнение SQL в рабочих потоках.utils/— ограничение длины/HTML, Telegram, Excel и журнал.tests/— регрессионные проверки без реальных запросов Telegram.
После установки зависимостей:
python -m unittest discover -s tests -vТесты создают отдельные базы в .test-data, имитируют Telegram и проверяют обработчики через настоящий диспетчер aiogram. Если рядом есть bot.db, один тест открывает её только для чтения, создаёт копию и проверяет миграцию и сохранение заявок на копии. Исходная база, .env и рабочий журнал не изменяются.
.env, базы, резервные копии, журналы и виртуальные окружения исключены через .gitignore. Не публикуйте их вместе с исходниками.