19 робочих місць під контролем з телефона власника
Зробив Telegram-бота з панеллю Mini App, через який власник офісу сам керує робочим сервером на 19 місць. Він бачить, хто працює, блокує вхід, додає працівників, перезапускає сервер і отримує попередження — без віддаленого робочого стола і без дзвінків адміну.




Що боліло
У власника вже був простий бот: увімкнути чи вимкнути сервер і подивитись його стан. Усе інше йшло через адміна: хто зараз працює, як заблокувати вхід, новий працівник, перезапуск. Власник хотів робити це сам, з телефона, без віддаленого робочого стола і консолі.
Вимоги замовника
- Усе з телефона і зрозуміло людині без технічної освіти.
- Кілька людей з різними правами. Ідея в тому, щоб власник сам вів адміністрування, а не дзвонив адміну.
- Критичні дії (живлення, блок, нові працівники) — лише з PIN-кодом.
- Блокування не має закривати чужу роботу: сеанс зберігається, людина продовжує з того ж місця.
- Видалення працівника — це відключення. Дані лишаються, доступ можна повернути.
Обмеження
- Старий бот працює до кінця тестів, і його не чіпаємо.
- Облікову програму офісу не чіпати: бот може лише читати її стан.
- Жодного стеження за екранами працівників і жодних скриншотів — принципова вимога.
- Уся робота віддалена: фізичного доступу до офісу не було.
- Кожна зміна — готовий скрипт PowerShell з бекапом, окремим скриптом відкату і перевіркою синтаксису до поставки.
Рішення
Бот живе на хості, де крутиться віртуальна машина офісу, і керує нею зсередини. Назовні він лише сам з’єднується — з Telegram і з тунелем, тому жоден порт за межу не відкритий.
Назовні бот лише сам з’єднується — з Telegram і з тунелем. Жоден порт за межу не відкритий.
- Один процес PowerShell 5.1. Він уже є в кожному Windows Server, нічого ставити не треба. Одночасно слухає Telegram і віддає панель. Довгі дії йдуть у фоні, щоб бот не зависав.
- Telegram Mini App. Це вебсторінка, яка відкривається всередині Telegram як застосунок. Чат лишився для коротких сповіщень і кнопок «Дозволити / Відхилити».
- Тунель Cloudflare. Сервер сам тягне з’єднання назовні, і панель отримує HTTPS-адресу. Сама панель слухає лише локальну адресу хоста.
- Перевірка підпису Telegram (HMAC-SHA256). Кожен запит з панелі несе криптографічну печатку від Telegram. Без токена бота її не підробити, тож чужий запит не пройде.
- PIN з хешем PBKDF2. Хеш — це «відбиток» PIN, з якого сам PIN не відновити. PBKDF2 навмисно повільний, щоб перебір не мав сенсу. Після 5 помилок — блок.
- PowerShell Direct. Канал між хостом Hyper-V і його віртуалкою, який взагалі не йде через мережу. Бот заходить під службовим записом, якому заборонено звичайний вхід. Пароль зашифрований DPAPI — вбудованим шифруванням Windows, прив’язаним до машини.
- WTS API. Вбудований інтерфейс Windows, який каже, хто підключений до сервера і скільки часу. З нього беруться статуси працівників.
- WireGuard. VPN, через який працівники підключаються. Бот додає і вимикає ключі на льоту, інші підключення не рвуться.
- Дані в JSON-файлах і журнал дій. База даних для десятка користувачів бота — зайва деталь, яку треба обслуговувати.
- Сторож. Задача планувальника запускає бота зі стартом системи і перевіряє його кожні 5 хв. Друга копія сама не запуститься.
Історія розробки
Бот ріс від заглушки до версії 1.11 за 20 оновлень. Кожне міняло лише файли нового бота і мало свій скрипт відкату.
Заглушка поруч зі старим ботом
Що сталось: розробляти треба на живому сервері і не зачепити робочий бот.
Як вирішили: окремий тестовий бот, окрема папка і задача. Старий бот лише читається. Сторож і захист від другої копії — з першого дня.
Меню, PIN і ролі
Що сталось: потрібні права доступу і захист дій.
Як вирішили: меню в чаті, PIN з хешем, видача доступу, журнал дій. Забутий PIN скидається одноразовим кодом, який видно лише на самому сервері.
Mini App замість кнопок у чаті
Що сталось: меню на кнопках у чаті виглядало занадто дешево.
Як вирішили: панель Mini App за погодженим прототипом, світла й темна тема. Спершу тимчасовий тунель з випадковою адресою, потім постійна адреса.
Хто і скільки працює
Що сталось: боту треба бачити, хто і скільки часу на сервері.
Як вирішили: службовий запис у віртуалці без права входу, пароль зашифрований. Список з іменами і статусами.
Блок входу без втрати роботи
Що сталось: власнику треба заборонити вхід, не згубивши чужу роботу.
Як вирішили: блок одного чи всіх: 15 хв, 1 год, доба або до розпорядження. Сеанс від’єднується, але зберігається. Блок знімається сам.
Робочий час
Що сталось: вхід уночі чи в неділю ніхто не бачив.
Як вирішили: графік у панелі (за замовчуванням Пн–Сб 08:00–19:00). Вхід поза ним — запит власнику «Дозволити / Відхилити». Відмова з PIN від’єднує і блокує до ранку.
Екран не вміщався в телефон
Що сталось: головний екран і вибір терміну блоку не вміщались у телефон.
Як вирішили: компактніший інтерфейс без прокрутки.
Новий працівник однією кнопкою
Що сталось: треба додавати людину без адміна.
Як вирішили: одна кнопка створює обліковий запис, VPN-ключ і підписаний ярлик підключення (підпис прибирає попередження Windows). Ліміт — 5 на добу. «Видалити» = вимкнути вхід і VPN, профіль лишається.
«Не вдалося додати працівника»
Що сталось: перший же тест — помилка. PowerShell не розрізняє регістр змінних, і мої $n та $N виявились однією. Номер нового користувача перезаписувався.
Як вирішили: перейменував змінні. Додав автоматичну перевірку коду на такі збіги перед кожною поставкою.
Власник адмініструє сам
Що сталось: власник має сам адмініструвати, включно з живленням. Рядок фільтрів доводилось гортати вбік.
Як вирішили: власник і адмін — повні права, живлення з PIN. Нова роль «менеджер»: працівники, блоки, графік. Фільтри переверстано без горизонтальної прокрутки.
Одноразові запрошення
Що сталось: замовник хотів додавати людей з адресної книги. Але бот не може написати першим тому, хто його не запускав, — це правило Telegram.
Як вирішили: одноразове посилання з роллю, дійсне 24 год. У базі лише його хеш. Активні запрошення можна скасувати.
Обслуговування без дзвінків адміну
Що сталось: лишались дії, за якими все одно дзвонили адміну.
Як вирішили: новий пароль, повідомлення всім на екран, перезапуск з попередженням за 1/5/10/15 хв і скасуванням. «Стан сервера»: процесор, пам’ять, диски, бекап, попередження.
Список працівників: з 12 с до 75 мс
Що сталось: список працівників перестав вкладатись у 12 с і падав за тайм-аутом. Причина: блокування Windows перевірялось окремо для кожного користувача.
Як вирішили: один запит на всіх — 75 мс. Першу перевірку стану зсунув на хвилину після старту, щоб не заважала.
Живий тест на справжньому сервері
Що сталось: треба перевірити все на справжньому сервері, не зачепивши людей.
Як вирішили: створили через бота тестового працівника. На ньому перевірили новий пароль і розсилку, після чого видалили й почистили. Перезапуск теж пройшов вживу.
Відлік до перезапуску
Що сталось: замовнику потрібен таймер до перезапуску, потім «перезапускається» і «онлайн».
Як вирішили: живий відлік у панелі. У чаті — одне повідомлення, яке оновлюється, а не потік нових. «Стан» і «Керування» винесені на головний екран.
Ліцензії й облікова програма
Що сталось: термін ліцензій віддаленого доступу і роботу облікової програми ніхто не відстежував.
Як вирішили: лічильник днів і попередження за 30/14/7/3/1/0 днів. Програма — тільки читання стану і нагадування щогодини, поки вона стоїть.
Позначка стану вибивалась з рядка
Що сталось: на вузькому екрані позначка стану вилазила за рядок.
Як вирішили: позначки в один стовпчик, коротші підписи.
Секрети — не в чат
Що сталось: таймер PIN ішов далі, поки PIN ще перевірявся. Паролі й архіви приходили в чат, де лежать завжди.
Як вирішили: таймер замирає на час перевірки. Секрети тепер у вкладці «Сповіщення» панелі на 15 хв, у чат — лише нагадування. Вкладки отримали значки. Перевірено на живому сервері.
Заміна старого бота без простою
Що сталось: новий бот має замінити старий так, щоб офіс жодної хвилини не лишався без керування.
Як вирішили: пакет переносу передає токен, переносить доступи і вимикає старий бот, не видаляючи його. Відкат вмикає старий назад. Передано власнику.
Дрібніші помилки, знайдені по дорозі
- У PowerShell список із функції записом
return , $listламав фільтрацію далі по конвеєру — запрошення й лічильник сповіщень рахувались неправильно. Тепер результат спершу кладеться в змінну. - Два стилі панелі мали однакову назву і зіпсували вигляд таймера. Перейменував.
- Заборона пересилання в Telegram блокувала б передачу архіву новому працівнику. Прибрав її, захист дає 15-хвилинний термін життя.
- Для перевірки інтерфейсу зібрав локальний стенд: справжній код бота працює проти імітації Telegram і сервера. Верстку панелі перевіряв там, до поставки.
Уроки
- Нове розгортати поруч зі старим, а не замість нього. Окремий тестовий бот дав тестувати на живому сервері, а власник жодного дня не лишався без керування.
- Малі оновлення з відкатом дешевші за одне велике. Помилки з додаванням працівника і тайм-аутом списку виправились наступним же оновленням, бо було видно, де саме зламалось.
- У PowerShell назви змінних не розрізняють регістр. Таку помилку очима не видно, тому тепер її ловить автоматична перевірка.
- Питати систему одним запитом, а не в циклі. Різниця була між тайм-аутом у 12 с і 75 мс.
- Секрети — не в чат. Чат зберігає все назавжди, а панель показує пароль 15 хв і забуває його.
- Обмеження платформи формують інтерфейс. Бот не може написати першим, тому замість адресної книги з’явились одноразові запрошення.
- Одне повідомлення, що оновлюється, краще за п’ять нових. Власник бачить стан перезапуску в одному місці, і чат не засмічується.
- Локальний стенд швидший за живий сервер. Верстку і сценарії видно за секунди, а живий сервер лишається для фінальної перевірки.
Результат
Версія 1.11 працює на живому сервері, бот передано власнику. Вживу перевірені:
- додавання, відключення і повернення працівника;
- новий пароль і розсилка на екрани;
- перезапуск з відліком і стан сервера;
- сповіщення і завантаження архіву нового працівника.
До бота є покрокова інструкція для власника.