Будь ласка, використовуйте цей ідентифікатор, щоб цитувати або посилатися на цей матеріал: https://er.chdtu.edu.ua/handle/ChSTU/6897
Повний запис метаданих
Поле DCЗначенняМова
dc.contributor.advisorПрокопенко , Тетяна Олександрівна-
dc.contributor.authorЗінченко, Максим Русланович-
dc.date.accessioned2026-01-25T22:31:36Z-
dc.date.available2026-01-25T22:31:36Z-
dc.date.issued2022-06-12-
dc.identifier.urihttps://er.chdtu.edu.ua/handle/ChSTU/6897-
dc.description.abstractУ кваліфікаційній роботі бакалавра розроблено телеграм бот для підтримки освітнього процесу закладу вищої освіти а саме Черкаського Державного Технологічного Університету. За допомогою аналізу бізнес домену проекту було прийнято рішення використовувати Node.js. Серверна частина (сервер та база даних) розроблялася з використанням наступних технологій програмування: TypeScript (типізований JavaScript) TypeORM, Docker, Docker-Compose, Docker-Machine, Nginx, база данних – реляційна PostgreSQL, клієнтська (сам бот) – використовуючи TypeScript (типізований JavaScript). Обсяг пояснювальної записки кваліфікаційної роботи бакалавра складає 113 сторінок, в тому числі вступ, 3 розділи, висновки до них, додаток та список використаних джерел. Робота містить 35 рисуноків та 38 інформаційних джерел. У першому розділі пояснювальної записки, досліджено актуальність проблеми, наведено переваги обраного проекту, проаналізовано предметну область розробленого проекту та проаналізовано аналоги. У другому – проведено аналіз технологій, в результаті чого підібрано інструменти за допомогою яких розроблено проект. Третій розділ описує практичну реалізацію проекту. А саме описується поетапна розробка боту, що було виконано під час розробки та наводяться приклади роботи. В результаті проведеної роботи отримано готового та робочого бота, який має заповнену базу даних, користувачів та систему керування.uk_UA
dc.language.isoukuk_UA
dc.subjectтелеграм ботuk_UA
dc.subjectDocker-Composeuk_UA
dc.subjectхостингuk_UA
dc.subjectPostgreSQLuk_UA
dc.subjectORMuk_UA
dc.subjectTypeORMuk_UA
dc.subjectNode.jsuk_UA
dc.subjectTypeScriptuk_UA
dc.subjectDockeruk_UA
dc.subjectDocker-Machineuk_UA
dc.titleРозробка телеграм боту для підтримки освітнього процесу закладу вищої освітиuk_UA
dc.typeBachelor Thesisuk_UA
Розташовується у зібраннях:126 Інформаційні системи та технології (Web-технології, web-дизайн)

Файли цього матеріалу:
Файл Опис РозмірФормат 
РЕП_БАК_Зінченко_WEB-1811.pdf
  Restricted Access
3.16 MBAdobe PDFПереглянути/Відкрити    Запит копії


Усі матеріали в архіві електронних ресурсів захищено авторським правом, усі права збережено.

Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ 
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ  
ФАКУЛЬТЕТ ІНФОРМАЦІЙНИХ ТЕХНОЛОГІЙ І СИСТЕМ 
 
КАФЕДРА ІНФОРМАЦІЙНИХ ТЕХНОЛОГІЙ ПРОЕКТУВАННЯ 
 
 
Пояснювальна записка 
до кваліфікаційної роботи бакалавра 
 
на тему: «Розробка телеграм боту для підтримки освітнього процесу закладу 
вищої освіти» 
 
Виконав студент 4 курсу, групи Web-1811,  
спеціальності 126 – Інформаційні 
системи та технології, 
освітня програма – Web- технології, 
Web-дизайн, 
 
Зінченко М. Р. 
Керівник д.т.н., проф. Прокопенко Т.О. 
Рецензент  
 
 
 
 
Черкаси – 2022 року 
 
АНОТАЦІЯ 
 
У кваліфікаційній роботі бакалавра розроблено телеграм бот для підтримки 
освітнього процесу закладу вищої освіти а саме Черкаського Державного 
Технологічного Університету. За допомогою аналізу бізнес домену проекту було 
прийнято рішення використовувати Node.js. Серверна частина (сервер та база 
даних) розроблялася з використанням наступних технологій програмування: 
TypeScript (типізований JavaScript) TypeORM, Docker, Docker-Compose, Docker-
Machine, Nginx, база данних – реляційна PostgreSQL, клієнтська (сам бот) – 
використовуючи TypeScript (типізований JavaScript). 
 
Обсяг пояснювальної записки кваліфікаційної роботи бакалавра складає 113 
сторінок, в тому числі вступ, 3 розділи, висновки до них, додаток та список 
використаних джерел. Робота містить 35 рисуноків та 38 інформаційних джерел. 
 
У першому розділі пояснювальної записки, досліджено актуальність проблеми, 
наведено переваги обраного проекту, проаналізовано предметну область 
розробленого проекту та проаналізовано аналоги.  
У другому – проведено аналіз технологій, в результаті чого підібрано інструменти 
за допомогою яких розроблено проект. 
Третій розділ описує практичну реалізацію проекту. А саме описується поетапна 
розробка боту, що було виконано під час розробки та наводяться приклади роботи. 
В результаті проведеної роботи отримано готового та робочого бота, який має 
заповнену базу даних, користувачів та систему керування. 
 
Ключові слова: бот, телеграм бот, хостинг, PostgreSQL, ORM, TypeORM, Node.js, 
TypeScript, Docker, Docker-Machine, Docker-Compose. 
 
2 
ANNOTATION 
 
In the qualification work of the bachelor was developed a telegram bot to support the 
educational process of a higher education institution, in this case, this is Cherkasy State 
Technological University. By analyzing the business domain of the project, I decided to 
use Node.js. The server part (server and database) was developed using the following 
programming technologies: TypeScript (pure JavaScript but with types) TypeORM, 
Docker, Docker-Compose, Docker-Machine, Nginx, database - relational PostgreSQL, 
client (the single bot) - using TypeScript (pure JavaScript but with types). 
 
The size of the explanatory note of the bachelor's qualification work is 113 pages, 
including the introduction, 3 sections, conclusions, appendix, and the list of used sources. 
The work contains 35 pictures and 38 sources of information. 
 
In the first section of the explanatory note, the subject area of the developed project is 
being analyzed. The business domain of the project is getting investigated. Analog 
projects were analyzed and analytics were conducted. It is substantiated why this topic 
was chosen, namely the development of bot telegrams. 
The second section describes the purpose and objectives of the project, an analysis of all 
possible technologies, and the tools selection according to these analyses. A short 
description of the project work planning (step by step) is given. 
The third section describes the practical implementation. It describes the bot development 
step-by-step, what was done during the development and what can be improved. As a 
result of the work, a ready-made and working bot was obtained, which has a complete 
database and control system soft. 
 
Keywords: bot, telegram bot, hosting, PostgreSQL, ORM, TypeORM, Node.js, 
TypeScript, Docker, Docker-Machine, Docker-Compose.
3 
 
ЗМІСТ  
ЗМІСТ 4 
ВСТУП 7 
1. АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ 8 
1.1. Дослідження актуальності проблеми 8 
1.2. Переваги чат-ботів перед мобільними програмами 12 
1.3. Призначення й мета Боту 13 
(i) Призначення проекту 13 
(ii) Мета створення сайту 13 
(iii) Цільова аудиторія 13 
1.4. Соціальні мережи та месенджери у яких можна зробити бота 14 
(i) Telegram 14 
(ii) Instagram 15 
(iii)Viber 15 
 
1.5. Аналіз аналогів 16 
(i) Веб-сайт з розкладом 16 
(ii) Мобільний додаток 18 
(iii) Порівняльна характеристика аналогів 21 
1.6. Висновок 23 
2. ВИБІР ЗАСОБІВ РЕАЛІЗАЦІЇ 23 
2.1. Мова програмування 23 
(i) JavaScript 23 
(ii) Python 26 
(iii) Ruby 28 
 
 
 ЧДТУ 22WEB-1805 ПЗ 
Зм Лист №  Підпис Дата 
Розроб.  Зінченко. М.Р. Розробка телеграм боту для підтримки Літ. Лист Листів 
 Перев. Прокопенко Т.О. освітнього процесу закладу вищої освіти. Н 2 113 
Реценз.    Пояснювальна записка ФІТІС, 
Н.контр.  кафедра ІТП, WEB-1811 
Затв. Прокопенко Т.О.  4 
 
2.2. СУБД 31 
(i) PostgreSQL 33 
(ii) Oracle Database 35 
(iii) MySQL 38 
(iv) MongoDB 39 
2.3. ORM 42 
(i) Sequelize 43 
(ii) TypeORM 45 
(iii) Prisma 46 
2.4.  Висновок 48 
3. РОЗРОБКА ПРОЕКТУ 51 
3.1. Стек та архітектура проекту 51 
(i) Серверна частина 52 
(ii) Клієнтська частина 52 
 (iii) DevOps частина 52 
3.2. Створення бази даних та таблиць 53 
(i) Таблиця користувачів 54 
(ii) Таблиця груп 56 
3.3. Створення серверної частини 57 
(i) Моделі 57 
(ii) Контролери 58 
(iii) Сервіси 59 
(iv) Маршрутизаця 60 
(v) Файлова структура 61 
 
 
 
 ЧДТУ 22WEB-1805 ПЗ 
Зм Лист №  Підпис Дата 
Розроб.  Зінченко. М.Р. 
 Розробка телеграм боту для підтримки Літ. Лист Листів 
Перев. Прокопенко Т.О. освітнього процесу закладу вищої освіти. Н 2 113 
Реценз. 3.3 .  Створення клієнтської частини Пояснювальна записка ФІТІС, 63 
Н.контр.  кафедра ІТП, WEB-1811 
Затв. Прокопенко Т.О.  5 
 
(i) Ініціювання боту 63 
(ii) Реєстрація групи 64 
(iii) Оновлення групи 65 
(iv) Видалення користувача 66 
(v) Отримання розкладу 67 
(vi) Відправка повідомлень групам 71 
(vii) Відправка повідомлень адміністратору 72 
(viii) Маніпулювання ботом 72 
(ix) Повідомлення про збій 74 
(x) Файлова структура 74 
3.4. Висновок 75 
ВИКОРИСТАНІ ДЖЕРЕЛА 77 
ПРОГРАМНІ ЗАСОБИ 81 
ДОДАТОК A 81 
 
 
ЧДТУ 22WEB-1805 ПЗ 
Зм Лист №  Підпис Дата 
Розроб. Зінченко. М.Р. Розробка телеграм боту для підтримки Літ. Лист Листів 
 Перев. Прокопенко Т.О. освітнього процесу закладу вищої освіти. Н 2 113 
Реценз.   Пояснювальна записка ФІТІС, 
Н.контр.  кафедра ІТП, WEB-1811 
Затв. Прокопенко Т.О.  6 
 
ВСТУП 
Сучасний світ важко уявити без Інтернету та комунікаційних інтернет-технологій. 
Натомість користувач, за допомогою мережі Інтернет, отримує набагато більше 
джерел нової інформації та всіляких послуг. Щодня людина користується мережею 
Інтернет за допомогою гаджетів (комп'ютерів, мобільних телефонів тощо) та може 
знайти майже все що їй потрібно для задоволення власних потреб, купити або 
замовити товар шляхом використання веб та мобільних додатків. З розвитком 
швидкості Інтернету кількість таких потреб та додатків стрімко зростає. 
Користувачі хочуть робити все не виходячи з дому або витрачаючи на все якумога 
менше часу. У такий час, важко уявити ефективну роботу будь-якого типу 
підприємства без власного сайту або мобільного додатку. Особливої популярності 
це набуло після спалаху пандемії COVID-19 – багато компаній та підприємств 
почали створювати веб та мобільні додатки щоб не втрачати заробіток, багато 
створено стартапів за час пандемії, які надають послуги доставки будь-чого, щоб 
 люди не виходили з дому та був менший шанс захворіти хворобою.  
 
Перед розробкою мобільного додатку треба прділити багато часу аналітиці та 
аналізу області використання додатку, зрозуміти які проблеми він вирішує та хто 
буде користуватися додатком. Мобильні додатки є дуже виправданими та 
корисними для користувача, якщо він користується ним кожного дня, якщо ж 
користувач не користується додатком кожного дня то існує ймовірність, що він не 
захоче його навіть встановлювати. Наприклад, додаток про прогноз погоди, 
користувачу зручно мати цей додаток у себе на мобільному і в декілька кліків 
дізнаватися прогноз, чого не скажеш про додаток для запису до перукаря, адже це 
трапляється не дуже часто, тому деякі користувачі не хочуть встановлювати такий 
додаток собі на смартфон, вони ліпше зроблять це через веб додаток.  
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 7 
З мн. Арк. №  докум. Підпис Дата  
 
Але є ще третій варіант – це розробка чат-боту. Чат боти можуть єкономити час 
людині або навіть заміняти людей роблячи певну роботу. 
 
Сам проект являє собою чат-бота для підтримки освітнього процесу закладу вищої 
освіти. За допомогою чат-боту студенти можуть отримувати розклад занять за 10 
секунд (відлік починаєть відколи вони розблоковую телефон). Таким чином не 
маючи потреби у встановленні додатку на телефон або користуванні веб ресурсом 
для розкладу. 
 
1. АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ 
1.1. Дослідження актуальності проблеми 
Бот — це програма, яка створена, щоб виконувати однотипні та повторювані 
завдання за певним алгоритмом, яка працює в інтернеті та виконує за людей деякі 
 завдання. Так виходить звільнити людські ресурси там, де їх недостатньо або їх 
використання невигідно, або просто для економії часу адже саме час це 
найцінніший ресурс людини. Програма працює через API зі швидкістю, яка у сотні 
тисяч разів більша за людську. Наприклад, бот може бути консультантом. Його 
інтегрують у чати та на сайти для створення там активності та відповідей на прості 
питання. Якщо він не справляється з поставленим завданням, то перенаправляє 
користувача на реальну людину з підтримки. Таким чином знижується 
навантаження реальних фахівців, зменшується потік нецільових звернень до них. 
 
Основне завдання будь-якого бота — звільнити людину від роботи, яку їй важко 
виконувати, наприклад, одноманітної, постійної повторюваної та просто нудної. 
Багато компаній впроваджують ботів, щоб заощадити кошти, тому що не потрібно 
платити фахівцям за виконання роботи яку може виконувати програма, зменшити 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 8 
З мн. Арк. №  докум. Підпис Дата  
 
час очікування та підвищити клієнтоорієнтованість. Головне –- налаштувати бота 
так, щоб він не відлякнув клієнтів мовчанням, неправильними відповідями чи 
дивною поведінкою. 
 
Основні сфери, у яких активно використовуються боти: 
● техпідтримка;  
● розрахунок вартості товару;  
● консультування з різних питань, наприклад, який кредит клієнту вибрати; 
прогрів користувача та підготовка його до покупки товару; 
● розвага відвідувачів; 
● лідогенерація - наприклад, бот проводить опитування клієнта, відповідає на 
його запитання, збирає дані у форму та експортує їх у crm-систему; 
● надання необхідної інформації, зокрема контактної; 
● відправка КП (комерційної пропозиції); 
 ● допомогу клієнту у виборі конкретного товару із двох моделей; 
● оформлення замовлення. 
 
Боту можна довірити дії: 
● реєстрацію нових користувачів; 
● ведення логів чатів; 
● контроль за правами учасників; 
● блокування користувачів за порушення правил, ненормативну лексику та 
загрози. 
 
У наші реалії користувачі все рідше відкривають пошту та менше проводять часу 
розглядаючи різні сайти. Не всі хочуть дзвонити чи ходити до магазинів. Але всім 
хочеться купити товар у пару кліків мишкою за кілька секунд. Їм ніколи чекати, 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 9 
З мн. Арк. №  докум. Підпис Дата  
 
коли прийде лист на пошту або зателефонує менеджер. Тому компанії, які 
користуються чат-ботами, отримують перевагу перед конкурентами. 
 
Чим використання бота може допомогти у бізнесі: 
● Бот заощадить гроші. Деякі обов'язки працівників будуть передані 
віртуальному помічнику. Можна більш продуктивно використовувати час 
працівників або не наймати ще одного працівника. 
● Бот збільшить продаж. Якщо клієнти будуть отримувати відповіді на всі свої 
запитання, то їхня лояльність збільшиться, а сумніви зникнуть. Більше клієнтів 
придбає товар. 
● Бот буде на зв'язку 24/7. Якщо потрібно терміново відповісти на просте 
запитання, то бот завжди на зв'язку, відповість миттєво. Він створить ефект 
присутності, який подобається багатьом клієнтам. 
● Бот зменшить час очікування. Ніхто не любить чекати у черзі. Чим швидше 
 клієнт отримає відповідь, тим вища ймовірність продажу. Усередині 
підрозділів організацій роботи допомагають у внутрішніх запитах, 
допомагають операторам. 
● Бот збільшить мобільний трафік. Це найпростіший спосіб бути встановленим 
у телефоні клієнта, тому що роботи не займають багато місця. 
● Бот повідомить про зміни в системі клієнтів та співробітників. А також нагадує 
необхідність закрити завдання або оплатити покупку. 
● Бот проведе активність. Якщо прописати йому сценарій та описати точку 
старту, то він впорається із проведенням конкурсу, визначенням переможця і 
навіть сам йому напише. 
 
Усі вміння бота можно розділити на два типа: 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 10 
З мн. Арк. №  докум. Підпис Дата  
 
● Обмежений. Відповідає на певну кількість чітких питань заданих 
користувачем. Такий бот не вчиться а просто виконконує поставлену перед 
ним задачу як даякий алгоритм. Коштує набагато дешевше у розробці. Його 
робота залежить тільки від того, що було закладено у його код (алгоритм). 
● Самонавчанний. Створюється складна система у якій працює нейронна 
мережа. Цей вид ботів коштує набагато дорожче та потребую багато часу на 
його розробку на навчання. Такий бот зможе вчититися на кожному клієнті та 
постійно удосконалюватися. 
 
Як зрозуміти, що ви переписуєтесь з ботом? 
● Бот швидко відповідає - щоб прочитати питання та відповісти на нього, боту 
потрібні частки секунди, люди так швидко писати та думати не можуть; 
● Бот повторює за людиною – боти часто повторюють те, що їм кажуть, вони 
імітують мислення; 
 ● Бот пише закінченими пропозиціями – насправді люди майже ніколи не 
використовують повні пропозиції, люди користуються смайликами, 
помиляються та скорочують слова; 
● Бот використовує дивний синтаксис - боти при спілкуванні з людиною 
намагаються повторювати їх мова і відтворюють одні й ті самі помилки, 
ставлять по дві прогалини, ставлять одну й ту саму букву не там; 
● Бот ставить особисті питання – щоб допомогти клієнту визначитися, бот може 
ставити багато запитань, у тому числі особисті; 
● Бот надсилає багато посилань – людям складно одночасно спілкуватися та 
шукати інформацію, щоб надіслати посилання людині, а бот часто спілкується 
саме так. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 11 
З мн. Арк. №  докум. Підпис Дата  
 
1.2. Переваги чат-ботів перед мобільними програмами 
Люди доволі часто порівнюють ботів з мобільними додатками і їм складно 
зрозуміти, що краще - бот або мобільний додаток. Давайте розглянемо це питання 
докладніше. 
 
Створити робота в рази дешевше, ніж створити цілий додаток. Після розробки 
доведеться займатися тривалим налагодженням та налаштуванням додатку. 
Причому це вкладення грошей це не гарантує, що після налаштування додаток буде 
цікавим для клієнтів. 
 
По статистиці GlobalWebIndex 80% користувачів активно користуються всього 
трьома додатками, серед яких є мінімум одна соціфльна мережа або/та мессенджер 
[1]. 
 
 У наш час люди не хочуть витрачати час щоб сробити щось зайвий раз а коли діло 
доходе до встановлення якогось додатку на телефон або на ПК люди постають 
перед сумнівами чи потрібен їм взагалі той додаток. Додаток займає місце та 
споживає ресурси на телефону або комп'ютеру. Наприклад, для запису до 
перукаря потенсійному клієнту не хочеться завантажувати додаток щобчерез 5 
хвилин його видаляти. Але він легко напише роботу в списку контактів. 
 
Зацікавити аудиторію через якісь нові додатки набагато складніше, ніж через уже 
встановлені. Це підтверджують дані із Quantcast [2]. Особливо це стосується 
месенджерів та соцмереж, які у заразє у кожного. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 12 
З мн. Арк. №  докум. Підпис Дата  
 
1.3. Призначення й мета Боту 
(i) Призначення проекту 
Проект призначений для підтримки освітнього процесу закладу вищої освіти 
Черкаського Державного Технологічного Університету. Завдяки даному проекту 
університет буде підтримувати найсучасніші засоби обміну інформацією.  
 
В результаті готовий проект буде представляти собою повністю робочого telegram 
бота, який полегшить студентське життя та набагато прискорить процес отримання 
розкладу та буде корисний для викладачів, тому що буде мати функціонал 
відправки повідомлень всім зареєстрованим або окремій групі, наприклад, щоб 
анонсувати щось важливе. 
(ii) Мета створення сайту 
Мета розробленого проекту полягає у створенні telegram боту для підтримки 
 
освітнього процесу закладу вищої освіти Черкаського Державного Технологічного 
Університету, яким зможуть користуватися не тільки студенти а й викладачі та 
отримувати розклад у два кліки без потреби витрачати час на пошук сайту абу 
встановлення третього додатку на телефон. 
(iii) Цільова аудиторія 
Цільова аудиторія даного проекту включає в себе студентів та викладачів 
університету. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 13 
З мн. Арк. №  докум. Підпис Дата  
 
1.4. Соціальні мережи та месенджери у яких можна зробити бота 
(i) Telegram 
Telegram — це дуже популярна кроссплатформенна система миттєвого обміну 
повідомленнями (месенджер) з функціями VoIP [3], що дозволяє обмінюватися 
текстовими, голосовими та відеоповідомленнями, стікерами та фотографіями, 
файлами багатьох форматів. Також можна здійснювати відео та аудіодзвінки та 
трансляції в каналах i групах, організовувати конференції, розраховані на багато 
користувачів групи та канали. За допомогою ботів функціонал додатку практично 
не обмежений. Клієнтські програми Telegram доступні для Android, iOS, Windows 
Phone, Windows, macOS та GNU/Linux. 
Кількість щомісячних активних користувачів сервісу станом січень 2021 року 
становить близько 500 млн чоловік [4]. 
 
Боти інтегруються для взаємодії з клієнтами, відповідають на їхні запитання, 
пропонують товари та способи доставки. 
Існує величезна кількість ботів в Telegram, тому що тут їх продати просто – це чати, 
в яких користувачі звикли листуватися.  
У Telegram боти застосовуються для:  
● розваги користувачів; 
● онлайн-консультацій; 
● пошуку інформації; 
● збирання інформації від користувачів; 
● покупка товарів (ніхто з конкурентів це не дозволяє). 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 14 
З мн. Арк. №  докум. Підпис Дата  
 
Щоб створити бота, необхідно спочатку зареєструвати його через внутрішній бот в 
Telegram. Називається бот @BotFather. Потім користуючись API документацією [5] 
можна написати код для боту. 
(ii) Instagram 
Instagram — соціальна мережа орієнтована на розміщення користувачами фото та 
відео та їх обміном, також користувачі можуть листуватися, відправляти відео чи 
аудио повідомлення, здійснювати трансляції. Також має «розумній» стрічку у якій 
користувачі бачуть індивідуально цікаві для них фото чи відео. 
Іноді для накрутки аудиторії люди користуються послугами ботів. Офіційно в 
самому Instagram немає ботів, за їх використання можуть заблокувати аккаунт, 
тому користуватися ним потрібно дуже обережно. 
 
В Instagram останнім часом стало з'являтися дуже багато олнайн магазинів. 
 Зазвичай ботів застосовують для прийому замовлень і спілкування з клієнтами. 
Виглядає як листування з користувачем, іноді з варіантами відповідей замість 
порожнього поля для користувача. Також використовують для збору статистики з 
облікового запису та моніторингу нових постів з певними хештегами. 
(iii)Viber 
програма-месенджер, яка дозволяє відправляти повідомлення, здійснювати відео- 
та голосові VoIP-дзвінки [3] через інтернет. Голосові виклики між користувачами 
зі встановленим Viber безкоштовні (оплачується лише інтернет-трафік за тарифом 
оператора зв'язку). Viber має можливість надсилати текстові, голосові та 
відеоповідомлення, документи, зображення, відеозаписи та файли, а також 
працювати в автономному режимі. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 15 
З мн. Арк. №  докум. Підпис Дата  
 
Для авторизації користувачів та пошуку контактів програма використовує номер 
телефону та передає вміст телефонної адресної книги (імена та телефони всіх 
контактів) на сервери корпорації Viber Media. Вони ж збирають інформацію про 
скоєні дзвінки та передані повідомлення, тривалість дзвінків, учасників дзвінків і 
чатів  з метою покращення якості обслуговування та в інших цілях. 
 
Для створення чат-боту у Viber потрібно спочатку його зареєструвати на 
платформі. 
Потім за допомогою Viber REST bot API документації [7] можна писати код бота. 
1.5. Аналіз аналогів 
Для успішної розробки проекту потрібно провести аналіз аналогів, щоб з'ясувати, 
які напрацювання вже існують і як вони використовуються на практиці та як їх 
можна удосконалити у своєму проекті. 
  
Знайдено аналогів: 2. 
(i) Веб-сайт з розкладом 
Розглянемо Веб-ресурс для отримання розкладу посилання на який: 
http://195.95.232.162:8082/cgi-bin/timetable.cgi. 
 
За допомогою цього веб-сайту студенти та викладачі можуть дізнаватися розклад 
занять групи або викладача на вказаний період часу заповнивши просту та 
зрозумілу форму. Розклад відображається у вигляді таблиць, що є досить зручним 
рішенням. 
 
У відображованому розкладі є наступні дані: 
● Назва групи; 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 16 
З мн. Арк. №  докум. Підпис Дата  
 
● День; 
● Нумерація пар; 
● Розклад дзвінків кожної пари; 
● Тип пари (лаб, екз, тощо); 
● Назва пари; 
● Аудиторія, у якій буде проведена пара; 
● Список груп, які будуть присутні на парі; 
● Викладач пари. 
 
Переваги: 
● Безкоштовний; 
● Має зрозумілий інтерфейс; 
● Надає можливість обрати викладача або групу або факультет; 
● Не містить реклами; 
 ● Отриманий розклад зручно читати завдяки групуванню його по таблицям. 
● Гарно налаоштоване SEO, сайт проіндексований пошуковим Google ботом 
[37]. 
 
Недоліки: 
● Має не захищене підключення, тобто немає SSL Certificate [38]; 
● Не має доменного імені в результаті цього посилання має негарний вигляд 
відображає IP-адресу серверу (195.95.232.162), на якому хостується сайт та 
порт (8082), на якому запущений код. Таке посилання тяжко запам'ятати, щоб 
вводити у пошук (рис 1.2.). 
● Не запомятовую групу. 
 
 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 17 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок - 1.1. – Інтерфейс розкладу. 
 
 
 
Рисунок - 1.2. – Незахижене підключення та IP серверу замість доменного імені. 
(ii) Мобільний додаток 
Розглянемо мобільний додаток “Мій розклад” для отримання розкладу посилання 
на скачування якого: 
https://play.google.com/store/apps/details?id=ru.candysoft.schedule&hl=uk&gl=US. 
 
За допомогою цього додатку студенти, які користуються Android смартфонами 
можуть дізнаватися розклад обравши потрібну групу у меню додатку. Розклад 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 18 
З мн. Арк. №  докум. Підпис Дата  
 
відображається у вигляді списку, що є оптимальним рішенням для мобільних 
додатків, так як таблиці не будуть уміщатися в экран смартфона. 
 
У відображованому розкладі є наступні дані: 
● Назва групи; 
● День; 
● Нумерація пар; 
● Розклад дзвінків кожної пари; 
● Тип пари (лаб, екз, тощо); 
● Назва пари; 
● Аудиторія, у якій буде проведена пара; 
● Список груп, які будуть присутні на парі; 
● Викладач пари. 
 
 Переваги: 
● Можна поділитись розкладом; 
● Має зрозумілий інтерфейс; 
● Запам'ятовує додану групу; 
 
Недоліки: 
● Не доступний для студентів, котрі мають iOS (телефони від Apple). 
● Не можна перевірити правильність/точність розкладу; 
● Отриманий не зовсім зручно читати, тому що немає візуального виділення у 
тексті пріорітетної інформації. Виділяються тільки номер пари, день та час 
(рис 1.3.); 
● Має рекламу (рис 1.3.). 
● Відображає рекламу без її фільтрації. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 19 
З мн. Арк. №  докум. Підпис Дата  
 
● Якщо користувач не бадає бачити рекламу він повинен платити гроші за 
“Преміум”. Це єдина сторінка у додатку, яка не містить реклами (рис 1.4.). 
● Не працює тайм-зона, наприклад, перебуваючи у Польщі, додаток показує ті ж 
самі години дзвінків, що і для України, а повинен показувати усі дзвінки на 
годину раніше. 
 
 
               
Рисунок - 1.3. – Інтерфейс відображення розкладу.  
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 20 
З мн. Арк. №  докум. Підпис Дата  
 
 
 
Рисунок - 1.4. – Сторінка покупки “Преміум” 
 
(iii) Порівняльна характеристика аналогів 
Зроблено тиблицю порівнялбних характеристик виже наведених аналогів 
(таблиця 1.1.). Задопомогою цієї таблиці було виявлено недоліки та переваги. 
 
Таблиця - 1.1. – Порівняльна характеристика виже наведених аналогів 
Назва критерію Аналоги 
(i) Веб-сайт (ii) Мобільний додаток 
Безкоштовність + - 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 21 
З мн. Арк. №  докум. Підпис Дата  
 
Наявність - + 
реклами 
Доступність + - 
(додаток недоступний для 
студентів з iOS) 
Швидкість 1-2 секунди 1-2 секунди 
завантаження 
розкладу на день 
Запам'ятовується - + 
додана група 
Наявність + - 
перевірки (сайт є офіційним 
 розкладу на джерелом) 
офіційному 
джерелі 
Читабельність + - 
розкладу (хороша читабельність (не дуже хороша 
завдяки даблицям та читабельність тому що все 
вдалому розбиттю на написано одним шрифтом, 
колонки) немає пріорітетності та 
виділення основного в 
тексті) 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 22 
З мн. Арк. №  докум. Підпис Дата  
 
1.6. Висновок 
У першому розділі розкрита актуальність проблеми, представлені переваги чому 
обрано саме цей шлях реалізації, розглянуто список соціальних мереж та 
месенджерів, у яких є можливість створити чат-бота. Описано мету сворення 
проекту. Зроблено аналіз аналогів та складено порівняльну характеристику, за 
допомогою якої можна починати проектування та розробку проекта, який буде 
міститу у собі переваги цих двох аналогів та вирішувати максимальну кількість їх 
недоліків. 
 
 2. ВИБІР ЗАСОБІВ РЕАЛІЗАЦІЇ 
2.1. Мова програмування 
Мова програмування — це штучна мова, створена для передачі команд машинам, 
зокрема комп'ютерам. Мови програмування використовуються для створення 
 програм, які контролюють поведінку машин, та для запису алгоритмів [17]. 
 
Для реалізації проекту розглядалися наступні мови програмування: Node.js, Python 
, Ruby. 
(i) JavaScript 
JavaScript призначений для швидкої обробки (input/output) данних та маніпуляцій з 
ними за короткий проміжок часу завдяки V8. На JavaScript вплинули багато мов, з 
метою зробити його схожим на Java, але простим у використанні для 
непрограмістів. можна порівняти з традиційними об’єктно-орієнтованими мовами. 
Крім того, JavaScript має ряд властивостей, притаманних функціональним мовам - 
функції як об'єкти першого класу, об'єкти як списки, перенесення, анонімні 
функції, блокування - що надає мові додаткову гнучкість.  
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 23 
З мн. Арк. №  докум. Підпис Дата  
 
Коли у команди NodeJS постало питання про вибір двигуна для JS, вони побачили 
що V8 набагато швидше і вибрали його.  
 
На сьогоднішній день JavaScript – це найшвидша мова, що інтерпретується у світі. 
 
Особливості: 
● Широка поширеність. Будь-який браузер, будь-яка операційна система 
підтримує цю мову. Не буде проблем із запуском, ні на стаціонарному 
комп'ютері, ні на мобільному пристрої. Розробнику сайту тепер не потрібно 
писати окремі версії для різних типів пристроїв, тестувати та боротися з 
несподіваними помилками. Достатньо перевірити працездатність коду у 
найпопулярніших браузерах. Будь-які, навіть маловідомі, браузери 
розробляються з можливістю виконання скриптів. 
● JavaScript додаток не вимагає встановлення на комп'ютер користувача. 
 Сценарії виконуються безпосередньо у браузері, коли завантажується 
сторінка. Користувач не тільки не робить якихось дій для цього, він навіть не 
помічає, що запустився веб-додаток. Тому не потрібно спеціальних знань та 
навичок, щоб використовувати продукти розробки JS. 
● Простий у вивченні. Саме цій мові успішно навчають дітей у школах. 
JavaScript відмінно підходить для навчання програмування ігор, мотивує та 
зацікавлює дітей. Також ця мова дозволяє бути впевненим, що написаний код 
може бути запущений будь-ким і на будь-якій платформі без встановлення 
додаткових програм та налаштування пристрою. Це незаперечна перевага для 
вчителя під час навчання великої кількості учнів. 
● Взаємодія із Office. Як правило, для написання коду тією чи іншою мовою 
необхідно завантажити та використовувати призначене для цього середовище, 
де згодом код буде написаний та скомпільований. Але редагувати код JS 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 24 
З мн. Арк. №  докум. Підпис Дата  
 
можна в редакторі Open Office і додатково не завантажувати спеціальне 
середовище розробки. Мова JavaScript також використовується для написання 
макросів для офісних програм. 
● Мова високого рівня. Це означає, що існує певний набір команд, за допомогою 
яких можна написати код. Не потрібно прописувати події на машинному коді. 
Мова високого рівня полегшує роботу програмісту, але при цьому не звужує 
спектр його можливостей. 
● Швидкий для користувача. Перевага швидкості JS отримує завдяки тому, що 
код не компілюється на стороні клієнта, адже скрипт виконується у браузері 
користувача. Це значно зменшує навантаження на сервер у порівнянні з веб-
застосунками, написаними іншими мовами. 
● Розвинена інфраструктура. На сьогоднішній день розробники можуть 
працювати з великою кількістю бібліотек та фреймворків (найпопулярнішими 
з них є React, Angular та Vue), декількома збирачами, допоміжними 
 бібліотеками (наприклад, Lodash) та генераторами статичних сайтів. 
 
Переваги: 
● Підтримка скриптів усіма популярними браузерами; повна інтеграція з 
версткою сторінок (HTML+CSS) та серверною частиною (backend). 
● Javascript дає змогу частково обробляти веб-сторінки на комп'ютерах 
користувача без запитів до сервера. Це заощаджує час та трафік, знижує 
навантаження на сервер. 
● Кількість готових рішень у відкритому доступі так зросла, що працювати з 
Javascript та його фреймворками стало задоволення. 
● Просте завдання можна вирішити за 5 хвилин, не треба виконувати зайву 
роботу. Для складних завдань є варіанти вирішення, можна підібрати 
найкраще, адаптувати. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 25 
З мн. Арк. №  докум. Підпис Дата  
 
● Заповнення форм, вибір дій, активація кнопок, перевірки введення, реагування 
на наведення/кліки миші тощо Це дає приголомшливий рівень юзабіліті. 
 
Недоліки: 
● Це обмеження функціональності за клієнта. 
● Нестрога типізація та вільне трактування. 
● Немає підтримки віддаленого доступу. 
● Доступність зловмисників. 
(ii) Python 
Python — це проста у використанні, але повноцінна мова програмування, яка надає 
набагато більше інструментів для структурування та підтримки великих програм, 
ніж оболонка. З іншого боку, він обробляє помилки краще, ніж C, і як мова дуже 
високого рівня, він має вбудовані високорівневі типи даних, такі як гнучкі масиви 
та словники, які потребують значного часу для ефективної реалізації на C.  
 
 
Python дозволяє розбивати програми на модулі, які можна перевикористовувати. 
Мова постачається з великою бібліотекою стандартних модулів, які можна 
використовувати як основу для нових програм або як приклади у вивченні мови. 
Стандартні модулі забезпечують інструменти для роботи з файлами, системними 
викликами, мережевими підключеннями і навіть інтерфейсами до різних графічних 
бібліотек. Це інтерпретована мова, яка економить багато часу, який зазвичай 
витрачається на компіляцію. Інтерпретатор можна використовувати в 
інтерактивному режимі, дозволяючи експериментувати з мовними можливостями, 
писати шаблони програм або тестувати функції при розробці знизу вгору. Python 
дозволяє писати дуже компактні та читабельні програми. 
 
Особливості: 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 26 
З мн. Арк. №  докум. Підпис Дата  
 
● Розширюваність. Ця функція дозволяє використовувати інші мови в коді 
Python. Це означає, що код Python може бути розширений і іншими мовами, 
тому він може бути легко вбудований в існуючий код, щоб зробити його 
надійнішим і розширити його можливості. Інші мови можна використовувати 
для компіляції нашого коду Python. 
● Портативність. Мова програмування переноситься, якщо вона дозволяє нам 
кодувати один раз і запускати будь-яку функцію. Отже, платформа, де вона 
була закодована і де вона працюватиме, не обов'язково має бути однаковою. 
Ця функція дозволяє використовувати одну з найцінніших функцій об'єктно-
орієнтованих мов – можливість повторного використання. Як розробнику, 
потрібно кодувати рішення і генерувати його байт-код, і не потрібно 
турбуватися про середовище, в якому воно запускатиметься. Наприклад, один 
може запустити код, розроблений в операційній системі Windows, в будь-якій 
іншій операційній системі, як-Linux Unix і т.д. 
 ● Масштабованість. Ця мова допомагає розробляти різні системи або програми, 
здатні обробляти обсяг роботи, що динамічно зростає. Ці типи додатків дуже 
допомагають у зростанні організації, оскільки вони досить сильні, щоб 
впоратися зі змінами певною мірою. 
● Відкритий код. Можна легко завантажити та використовувати його за 
необхідності, а також поділитися ним з іншими. Таким чином це покращується 
щодня. 
● Інтегрованість. Python може бути легко інтегрований з іншими доступними 
мовами програмування, такими як C, C++, Java тощо. Це дозволяє кожному 
використовувати його для покращення функціональності існуючих програм та 
підвищення його надійності. 
● Автоматичне складання сміття. Python також запускає автоматичне складання 
сміття для відмінного управління пам'яттю та продуктивністю. Завдяки цьому 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 27 
З мн. Арк. №  докум. Підпис Дата  
 
пам'ять може використовуватися по максимуму, що робить програму більш 
стійкою. 
 
Переваги: 
● Має велике ком'юніті; 
● Безліч доступних середовищ розробки; 
● Має єдиний стандарт для написання коду; 
● Універсальний; 
● Швидко зростаючий; 
● Затребуваний на ринку; 
 
Недоліки: 
● Python є не найшвидшим серед мов програмування. Швидкість виконання 
програм може бути нижчою; 
 ● Не найзручніша мова для мобільних розробок; 
● Через гнучкість типів даних споживання пам'яті Python не мінімальне. 
(iii) Ruby 
 
Ruby — інтерпретована повністю об'єктно-орієнтована мова програмування з 
чіткою динамічною типізацією. Він поєднує в собі Perl-подібний синтаксис із 
об'єктно-орієнтованим підходом. Також деякі риси запозичені з мов програмування 
Python, Lisp, Dylan та CLU.  
Кросплатформова реалізація інтерпретатора мови Ruby поширюється на умовах 
відкритого програмного забезпечення. Код, написаний на Ruby, може бути 
зрозумілий навіть людині, яка не розуміється на програмуванні. На RoR були 
створені такі проекти як Redmine, Twitter, Shopify, Basecamp, GitHub, Kickstarter, 
Airbnb та інші. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 28 
З мн. Арк. №  докум. Підпис Дата  
 
 
Зі зростанням Node.js популярність Ruby дещо зменшилася, але технологічні 
стартапи часто використовують його завдяки простоті прототипування. 
 
Зараз нерідко в мережі публікуються статті про злам різних ресурсів. Розробники 
фреймворку Ruby on Rails дуже серйозно поставилися до проблеми захисту даних. 
У RoR спочатку є шифрування паролів, даних кредитних карток та інших 
особистих даних користувача, також виключені SQL ін'єкції та XSS атаки. Усі 
вхідні установки екрануються за замовчуванням. 
Віна не вимагає попереднього оголошення змінних, хоча бажано, щоб 
інтерпретатор призначав змінній порожнє значення nil (тоді інтерпретатор знає, що 
ідентифікатор вказує на змінну, а не на назву методу). Мова використовує прості 
механізми для визначення області застосування. Приклад: просто var — локальна 
змінна, @var — змінна екземпляра (член або поле об’єкта класу), @@ var — змінна 
 класу, $var — глобальна змінна. 
 
Особливості: 
● Інтерпретованість. Мова програмування Рубі інтерпретується, написаний на 
ньому код обробляється в момент виконання спеціальною програмою-
інтерпретатором, яка має кросплатформенну реалізацію і поширюється на 
умовах відкритого ПЗ. Таким чином, вивчати цю мову і програмувати нею 
можна на будь-якій сучасній операційній системі. Це забезпечує Ruby певні 
переваги порівняно з іншими поширеними ЯП: створені на ньому програми 
мають менший розмір, швидше працюють і не залежать від особливостей 
використовуваної платформи. Мова розроблялася на Linux, але працює на 
багатьох версіях Unix, DOS, Microsoft Windows (зокрема, Win32), Mac OS, 
BeOS, OS/2 і т.д. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 29 
З мн. Арк. №  докум. Підпис Дата  
 
● Динамічна типізація. Ця особливість мови допомагає визначати типи даних та 
здійснювати їх компіляцію - тобто, на етапі виконання програми 
перетворювати написаний розробником програмний код на набір машинних 
кодів. Динамічна типізація помітно прискорює та спрощує процес розробки 
програм та додатків. 
● Шаблони. Розробникам на Рубі не потрібно писати HTML-код самостійно, 
натомість вони можуть скористатися допомогою генераторів та 
шаблонізаторів коду. Такі ж допоміжні інструменти доступні на Ruby on Rails, 
за допомогою яких можна помітно прискорити роботу з front-end. Серед 
найбільш відомих шаблонізаторів виділяють ERB, HAML, SLIM, Liquid. 
● MVC. Однією з найважливіших особливостей Ruby вважається наявність 
схеми поділу компонентів програми (Model-View-Controller, MVC). Вона 
допомагає розробникам використовувати типові підходи до розробки різних 
проектів завдяки одноманітності їх структури та архітектури. MVC сприяє 
 прискоренню розробки та внесення змін до ПЗ, дозволяє швидше знаходити та 
виправляти помилки в коді. З його допомогою нові учасники команди можуть 
швидше розібратися в чужому коді та підключитися до робочого процесу. 
● Бази даних. Технологія ORM, що використовується у фреймворку Ruby on 
Rails, дає можливість писати універсальний код, що підходить для будь-якої 
бази даних, що підтримується. Таким чином, «рубісти» можуть не вивчати з 
нуля всі сучасні типи БД, достатньо мати досвід роботи з однією реляційною 
базою, найбільш прийнятною вважається PostgreSQL. 
● Деплой та тестування. Ruby містить низку корисних інструментів не тільки для 
написання, але і для розгортання програмного коду. Наприклад, у ньому є 
вбудована система автоматизації для рефакторингу, що дозволяє виконати цю 
процедуру за допомогою однієї команди. Також тут представлений набір 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 30 
З мн. Арк. №  докум. Підпис Дата  
 
інструментів тестування контролю якості коду, зокрема Rspec, Minitest, 
Cucumber, Capybara. 
 
Переваги: 
● Масовий спектр товариського ком'юніті. 
● Надається можливість використовувати тільки бібліотеки та модулі, які 
необхідні. 
● Є велика кількість необхідних бібліотек, які відразу можна використовувати. 
● Інтернет наповнений усіма видами інформації про Ruby, яка викладена у 
структурованому та відсіяному вигляді. 
● І, звичайно, не можна не згадати найпопулярніший фреймворк Ruby on Rails. 
 
Недоліки: 
● Ruby повільний (до версії 2.0 був найповільнішим у світі). 
 ● Ruby - представник write-only мов (тобто читати його на порядки складніше, 
ніж писати на ньому). 
● Він не призначений для розробки надійних рішень корпоративного рівня. 
● Немає хоча б мінімальної типізації. 
● Ruby переускладнена мова (наприклад, у нього цілих 4 способи 
сконтактенувати рядок) і кожна версія додає цукру. 
2.2. СУБД 
СУБД (система управління базами даних) є комплексом ПЗ, за допомогою якого 
можна створювати бази даних (БД) і проводити над ними різні операції: 
оновлювати, видаляти, вибирати, редагувати і т. д. СУБД гарантує безпеку, 
цілісність, безпеку зберігання даних та дозволяє видавати доступ до 
адміністрування БД [29]. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 31 
З мн. Арк. №  докум. Підпис Дата  
 
 
СУБД забезпечує: 
● роботу з даними, розміщеними на зовнішніх накопичувачах; 
● роботу з даними, що знаходяться в ОЗП із застосуванням дискового кешу; 
● ведення звітності стосовно: резервування, редагування, бекапу даних тощо; 
● підтримку різних мов баз даних (для роботи та визначення конкретних типів 
даних). 
 
СУБД складається з: 
● Ядра. Підтримує звітність, відповідає за управління даними у ОЗУ та на 
зовнішніх накопичувачах. 
● Процесор мови БД. Дозволяє оптимізувати запити на створення та 
редагування даних. 
● Підсистеми підтримки часу виконання. Дозволяє інтерпретувати ПЗ для 
 підтримки роботи з БД, створювати інтерфейси взаємодії з СУБД. 
● Допоміжного ПЗ. Набір утиліт, що дозволяють розширити можливості 
взаємодії з СУБД (зокрема, обслуговування). 
 
СУБД поділяються на окремі типи, спираючись на моделі даних, методи надання 
доступу до БД та рівню розподіленості. 
 
Залежно від моделі даних СУБД бувають: 
● мережевими; 
● ієрархічними; 
● реляційними; 
● об'єктно-реляційними; 
● об'єктно-орієнтованими. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 32 
З мн. Арк. №  докум. Підпис Дата  
 
 
Для реалізації проекту розглядалися наступні бази даних: PostgreSQL, MySQL, 
MongoDB. 
(i) PostgreSQL 
 
PostgreSQL — це потужна об'єктно-реляційна база даних з відкритим вихідним 
кодом яка розроблюється та активно підтримується упродовж 30-ти років, завдяки 
цьому вона заслужила хорошу репутацію за надійність та стабільність. Мова SQL 
[24]. 
 
Однією з найсильніших сторін СУБД PostgreSQL є архітектура. Як і у випадках з 
багатьма комерційними СУБД, PostgreSQL можна застосовувати в середовищі 
клієнт-сервер – це надає безліч переваг і користувачам, і розробникам. 
 
 
СУБД PostgreSQL використовує для баз даних реляційну модель, підтримуючи 
стандартну мову запитів SQL. При цьому PostgreSQL надає широкий спектр 
можливостей. Можна сказати, що Postgres має майже всі можливості, що існують в 
інших базах даних (як комерційних, так і Open Source), а також поруч додаткових. 
 
Сьогодні СУБД PostgreSQL працює майже на всіх UNIX-платформах, у тому числі 
і на UNIX-подібних системах (FreeBSD та Linux). Ви зможете використовувати цю 
базу даних і Windows NT Server, і Windows 2000 Server. 
 
Особливості: 
● Надійність. Надійність СУБД PostgreSQL перевірено та доведено. Вона 
забезпечується відповідністю принципам ACID (атомарність, ізольованість, 
несуперечність, збереження даних), багатоверсійністю, наявністю Write Ahead 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 33 
З мн. Арк. №  докум. Підпис Дата  
 
Logging (WAL) - загальноприйнятого механізму протоколювання всіх 
існуючих транзакцій. Сюди варто віднести і можливість відновлення бази 
даних Point in Time Recovery (PITR), реплікацію, підтримку цілісності даних 
на рівні схеми. 
● Продуктивність. У СУБД PostgreSQL вона заснована на застосуванні індексів, 
наявності гнучкої системи блокувань та інтелектуального планувальника 
запитів, використанні системи керування буферами пам'яті та кешування. Не 
варто забувати і про відмінну масштабованість при конкурентній роботі. 
● Розширюваність. Для СУБД PostgreSQL це означає, що користувач може 
настроїти систему за допомогою визначення нових функцій, типів, мов, 
агрегатів, індексів та операторів. А об'єктна орієнтованість СУБД PostgreSQL 
дає можливість переносити логіку програми на рівень бази даних, а це, у свою 
чергу, помітно спрощує розробку клієнтів, адже вся бізнес-логіка знаходиться 
у БД. При цьому функції Postgres однозначно визначаються назвою, типами і 
 числом аргументів. 
● Підтримка SQL. Її вже згадували, проте крім головних можливостей, які 
притаманні будь-якій SQL-базі, PostgreSQL підтримує схеми, підзапити, 
зовнішні зв'язки, правила, курсори, успадкування таблиць, тригери та багато 
чого ще. 
● Підтримка численних типів даних. СУБД PostgreSQL підтримує чисельні (цілі, 
грошові, з фіксованою/плаваючою точкою), булеві, символьні, складові, 
мережеві типи даних, а також перерахування, типи «дата/час», геометричні 
примітиви, масиви, XML- та JSON-дані. Плюс можна створювати типи даних. 
 
Переваги: 
● Є масштабованим рішенням і дозволяє обробляти терабайти даних. 
● Підтримує формат JSON. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 34 
З мн. Арк. №  докум. Підпис Дата  
 
● Існує безліч зумовлених функцій. 
● Доступний ряд інтерфейсів. 
 
 
Недоліки: 
● неефективність архітектури щодо виконання запису, 
● неефективна реплікація даних, 
● випадки пошкодження таблиць, 
● проблеми з MVCC на репліках, 
● Проблеми з оновленням. 
 
Ідеально підходить для додатків з обмеженим бюджетом, але вимагає залучення 
кваліфікованих спеціалістів, коли потрібно вибрати унікальний інтерфейс і 
використовувати json. 
 
(ii) Oracle Database 
Oracle Database — це об'єктно-реляційна система управління базами даних (СУБД) 
від компанії Oracle. Вона використовується для створення структури нової бази, її 
наповнення, редагування вмісту та відображення інформації. Продукт Oracle 
Database часто називають просто Oracle за назвою компанії [25]. 
 
Oracle використовує механізм блокування для керування одночасним доступом до 
даних. При зміні інформації сервер бази даних блокує інформацію, що змінюється, 
поки зміни не будуть завершені або скасовані. Поки таке блокування не буде знято, 
жодна інша трансакція не може змінити заблоковані дані. Таким чином, 
забезпечується цілісність даних. Oracle забезпечує "не розширюване" блокування 
окремих записів таблиці. Деякі сервери баз даних "розширюють" блокування на 
кілька записів або навіть на всю таблицю відразу, Oracle завжди блокує ті записи 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 35 
З мн. Арк. №  докум. Підпис Дата  
 
таблиці, які змінюються. Оскільки Oracle самостійно зберігає інформацію про 
блоковані записи, можливе блокування необмеженої кількості записів, таким 
чином багато користувачів може працювати одночасно, без непотрібних затримок. 
 
Особливості: 
● Паралелізм. Головна турбота систем управління розрахованими на багато 
користувачів базами даних - як управляти паралелізмом, тобто. одночасним 
доступом безлічі користувачів до тих самих даних. Якщо механізм 
паралелізму працює некоректно, дані може бути помилково змінені. 
Один варіант організації цього механізму полягає в наступному - зобов'язати 
всіх користувачів чекати на свою чергу. При цьому мета системи керування – 
скоротити час очікування користувача своєї черги. Всі запити мови 
маніпуляції даними повинні оброблятися під пильним наглядом, всі 
деструктивні взаємодії між паралельними трансакціями повинні припинятися. 
 Деструктивними взаємодіями називаються взаємодії, які неправильно 
модифікують дані. У цьому зберігається цілісність даних без зниження 
продуктивності. Oracle реалізує цей метод, використовуючи різні блокування 
та багатоваріантну стійку модель. Ці можливості ґрунтуються на концепції 
трансакції. 
● Стійке читання, записи відкату та трансакції. Щоб керувати багатоваріантною 
стійкою моделлю, Oracle повинен забезпечити стійке читання, якщо таблиця 
оновлюється під час виймання даних. Під час оновлення (update) початкові 
дані змінюються та зберігаються у записі відкату бази даних. Доки трансакція 
оновлення не буде завершена (commit), вибірка оновлених даних, виконана з 
іншої сесії, поверне початкові значення. Щоб створити read-consistent view 
(подання стійкого читання) даних таблиці, Oracle використовує поточну 
інформацію із SGA та інформацію із записів відкату. Зміни, зроблені 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 36 
З мн. Арк. №  докум. Підпис Дата  
 
трансакцією, фіксуються лише після завершення (команда commit), і лише 
після цього змінені дані будуть помітні в інших сесіях. 
● Трансакції, що не змінюють дані. Трансакція – це ключовий фактор стратегії 
забезпечення цілісності даних у СУБД Oracle. За промовчанням, Oracle 
гарантує стійке читання на рівні запиту. Набір даних, що повертається 
поодиноким запитом, блокується лише на час вибірки. Однак, у деяких 
випадках вам може знадобитися стійке читання на рівні трансакції. Це 
можливість виконати безліч запитів у межах однієї трансакції, причому можна 
бути впевненим, під час виконання наступних запитів дані, які повернув 
перший запит, не зміняться, тобто. не буде враховано змін, зроблених за цей 
час завершеними трансакціями. Якщо вам потрібно зробити кілька запитів із 
різних таблиць, і ви не змінюєте ці таблиці, використовуйте трансакції read-
only. 
 
 Переваги: 
● Найсвіжіші інновації та вражаючий функціонал вже запроваджено у цьому 
продукті, оскільки компанія Oracle прагне тримати планку навіть на тлі 
інших розробників СУБД. 
● Oracle є вкрай надійною, фактично це зразок надійності серед подібних 
систем. 
 
Недоліки: 
● Вартість Oracle може бути непомірно високою, особливо для невеликих 
організацій. 
● Система може вимагати значних ресурсів вже відразу після установки, тому 
можливо, потрібно модернізувати обладнання для впровадження Oracle. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 37 
З мн. Арк. №  докум. Підпис Дата  
 
Ідеально підходить для великих додатків, які працюють з величезними базами 
даних та різноманітними функціями. 
(iii) MySQL 
MySQL – одна з найпопулярніших СУБД для веб-додатків. Фактично є стандартом 
для веб-серверів, які працюють під управлінням операційної системи Linux. 
MySQL – це безкоштовний пакет програм, проте нові версії виходять постійно, 
розширюючи функціонал та покращуючи безпеку. Існують спеціальні платні версії, 
що призначені для комерційного використання. У безкоштовній версії 
максимальний акцент робиться на швидкість і надійність, а не на повноту 
функціоналу, який може стати і перевагою і дефіцитом - залежно від галузі 
застосування [26]. 
 
Розробку та підтримку MySQL здійснює корпорація Oracle, яка отримала права на 
торгову марку разом із поглиненою Sun Microsystems, яка раніше придбала 
 
шведську компанію MySQL AB. Продукт розповсюджується як під GNU General 
Public License, так і під власною комерційною ліцензією. Крім цього, розробники 
створюють функціональність на замовлення ліцензійних користувачів. Саме 
завдяки такому замовленню майже в ранніх версіях з'явився механізм реплікації. 
 
Ця СУБД дозволяє вибирати різні двигуни для системи зберігання, які дозволяють 
змінювати функціонал інструменту та виконувати обробку даних, що зберігаються 
у різних типах таблиць. Гнучкість СУБД MySQL забезпечується підтримкою 
великої кількості типів таблиць: користувачі можуть вибрати як таблиці типу 
MyISAM, що підтримують повнотекстовий пошук, і таблиці InnoDB, підтримують 
транзакції лише на рівні окремих записів. Понад те, СУБД MySQL постачається зі 
спеціальним типом таблиць EXAMPLE, демонструючим принципи створення 
нових типів таблиць. Завдяки відкритій архітектурі та GPL-ліцензування, в СУБД 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 38 
З мн. Арк. №  докум. Підпис Дата  
 
MySQL постійно з'являються нові типи таблиць. Вона також має простий у 
використанні інтерфейс і пакетні команди, які дозволяють зручно обробляти 
величезні обсяги даних. Система неймовірно надійна і прагне підпорядкувати собі 
всі доступні апаратні ресурси. 
 
Переваги: 
● Поширюється безкоштовно; 
● Чудово документована; 
● Пропонує багато функцій, навіть у безкоштовній версії; 
● Пакет MySQL включений у стандартні репозиторії найпоширеніших 
дистрибутивів операційної системи Linux, що дозволяє встановлювати її 
просто; 
● Підтримує набір інтерфейсів користувача; 
● Може працювати з іншими базами даних, включаючи DB2 та Oracle. 
  
Недоліки: 
● Потрібно витрачати багато часу та зусиль, щоб змусити MySQL виконувати 
нескладні завдання, хоча інші системи роблять це автоматично, наприклад: 
створювати інкрементні резервні копії. 
● Відсутня вбудована підтримка XML або OLAP. 
● Для безкоштовної версії доступна лише платна підтримка. 
 
Ідеально підходить для: додатків, які потребують надійного інструменту 
управління базами даних, але безкоштовні. 
(iv) MongoDB 
Ще одна безкоштовна система, яка має комерційну версію – MongoDB. Вважається 
одним із класичних прикладів NoSQL-систем, використовує JSON-подібні 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 39 
З мн. Арк. №  докум. Підпис Дата  
 
документи та схему бази даних. Написана мовою C++. Вона призначена для 
програм, які використовують як структуровані, так і неструктуровані дані. Ядро є 
дуже гнучким і працює при підключенні бази даних до програм через драйвери 
MongoDB. Існує широкий вибір доступних драйверів, тому легко знайти драйвер, 
який працюватиме з необхідною мовою програмування. 
 
Оскільки система MongoDB не була розроблена для обробки моделей реляційних 
даних (хоча може це виконувати), можуть виникнути проблеми продуктивності, 
якщо спробувати використовувати її таким чином. Однак, двигун призначений для 
обробки різних даних, які не можна віднести до реляційних, і може добре 
справлятися там, де інші двигуни працюють повільно або безсилі. 
 
MongoDB 5.0 - це остання версія (на травень 2022 р.), і вона має нову систему 
движків зберігання, що підключається. Документи можуть бути перевірені в 
 процесі оновлення або виконання вставок, а функції текстового пошуку покращено. 
Нова здатність часткового індексування може призвести до більш високої 
продуктивності зменшуючи розмір індексів. 
 
Особливості: 
● Документоорієнтована база – збереження даних у форматі документів замість 
формату реляційного типу, це робить MongoDB дуже гнучкою та адаптованою 
до бізнес-вимог. Можливість зберігання різних типів даних особливо важлива 
під час роботи з великими даними, які збираються з різних джерел і не лягають 
в одну структуру. 
● Спеціальні запити — MongoDB підтримує пошук за полями, діапазонні запити 
та пошук за регулярними виразами. Можуть бути зроблені запити повернення 
певних полів у документах. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 40 
З мн. Арк. №  докум. Підпис Дата  
 
● Індексація — Ви можете створити індекси для покращення продуктивності 
пошуку в MongoDB. Будь-яке поле в документі може бути проіндексовано. Це 
забезпечує високу швидкість роботи СУБД. 
● Реплікація - ця СУБД може забезпечити високу доступність за допомогою 
наборів реплік. Набір реплік складається з двох або більше примірників 
MongoDB. Кожна репліка набору може бути ролі первинної чи вторинної. 
Первинна репліка – головний сервер, який взаємодіє з клієнтом та виконує всі 
операції читання/запису. Вторинні репліки зберігають копію даних первинної 
репліки за допомогою вбудованої реплікації. Якщо з первинною реплікою 
щось трапилося, відбувається автоматичне перемикання на вторинну репліку, 
потім стає основним сервером. 
● Балансування навантаження - MongoDB використовує концепцію шардингу 
для горизонтального масштабування за допомогою поділу даних між кількома 
екземплярами БД. Вона може працювати на декількох серверах, балансуючи 
 навантаження та/або дублюючи дані, щоб підтримувати працездатність 
системи у разі апаратного збою. 
● Можливість розгорнути у хмарі – ви отримуєте готову до роботи, оптимально 
налаштовану, масштабовану та керовану базу даних на запит за дві хвилини. 
● Доступність - MongoDB підтримує всі популярні мови програмування, її 
можна використовувати безкоштовно як Open Source рішення. 
 
В одному документі можуть бути поля різних типів даних, дані не потрібно 
наводити до одного типу. Основна перевага MongoDB полягає в тому, що вона 
може зберігати будь-які дані, але ці дані мають бути у форматі JSON. 
 
Переваги:  
● Швидкість та простота у використанні 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 41 
З мн. Арк. №  докум. Підпис Дата  
 
● Двигун підтримує json та інші традиційні документи NoSQL. 
● Дані будь-якої структури можуть бути збережені/прочитані швидко та легко. 
 
Недоліки: 
● SQL не використовується як мови запитів. 
● Інструменти для перекладу SQL-запитів MongoDB доступні, але їх слід 
розглядати саме як доповнення. 
● Програма установки може тривати багато часу. 
 
 
Підходить для додатків, які працюють із різнорідними даними, які важко 
піддаються класифікації. Для застосування потрібні висококласні фахівці. 
2.3. ORM 
 ORM (Об'єктно-реляційне відображення) - це метод програмування, в якому 
дескриптор метаданих використовується для підключення об'єктного коду до 
реляційної бази даних. . ORM перетворює дані між системами типів, які не можуть 
співіснувати в реляційних базах даних та мовах OOP [18]. 
 
Завдяки цій технології розробники можуть використовувати мову програмування, 
з якою їм зручно працювати з базою даних, замість написання операторів SQL або 
процедур, що зберігаються. Це може значно прискорити розробку програм, 
особливо на початковому етапі. 
 
ORM вирішує невідповідність об'єктного коду та реляційної бази даних трьома 
підходами:  
● знизу вгору; 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 42 
З мн. Арк. №  докум. Підпис Дата  
 
● зверху вниз; 
● зустрічаючись посередині. 
Кожен підхід має свою частку переваг та недоліків. Вибираючи найкраще 
програмне рішення, розробники повинні повністю розуміти середовище та вимоги 
до дизайну. 
 
Переваги ORM: 
● Спрощена розробка, оскільки вона автоматизує перетворення об'єкта на стіл 
та таблицю в об'єкт, що призводить до зниження витрат на розробку та 
обслуговування 
● Менше коду в порівнянні з вбудованими SQL та рукописними збереженими 
процедурами 
● Прозоре кешування об'єктів у рівні програми, покращуючи продуктивність 
системи 
 ● Оптимізоване рішення, що робить додаток швидшим та легшим у 
обслуговуванні 
(i) Sequelize 
Sequelize – це ORM (Object-Relational Mapping – об'єктно-реляційне відображення 
або перетворення) для роботи з такими СУБД (системами управління 
(реляційними) базами даних, Relational Database Management System, RDBMS), як 
Postgres, MySQL, MariaDB, SQLite та MSSQL. Це далеко не єдина ORM для роботи 
з названими базами даних (далі - БД), але, на мій погляд, одна з найпоширеніших і, 
як то кажуть, "battle tested" (перевірених часом) [19]. 
 
Sequelize підтримує необроблені оператори SQL, що дає розробникам гнучкість 
при написанні складних та високопродуктивних операторів SQL. Результати також 
можна порівняти з екземплярами сутності об'єкта. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 43 
З мн. Арк. №  докум. Підпис Дата  
 
 
 
Рисунок - 2.1. – Команди для встановлення Sequelize. 
 
Переваги: 
● Узагальніть абстракції від тонких відмінностей між різними реалізаціями SQL; 
● У Sequelize реалізовано проміжне програмне забезпечення, яке дозволяє 
налаштувати повідомлення про помилку для збоїв у кожному полі; 
● Sequelize надає бібліотеку на основі обіцянок, яка допомагає краще керувати 
 
винятком і результатом, коли він є неочікуваним; 
● Має можливість синхронізувати базу даних на змінених моделях. Таким 
чином, він змінить або автоматично створить таблиці для вас; 
Має можливість запитувати всі складні об’єднання для вас. Вам просто 
потрібно навчитися з’єднувати об’єкти один з одним. 
 
Недоліки: 
● Sequelize іноді генерує складні запити. Визначення моделі іноді неправильно 
розуміє відношення, коли відношення has-one; 
● Sequalize не підтримує NoSQL; 
● Є деякі дивні винятки, коли щось стає, і ви хочете створити більше підключень 
до БД. Для цього можна використовувати пул підключень. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 44 
З мн. Арк. №  докум. Підпис Дата  
 
(ii) TypeORM 
TypeORM — це ORM, який може працювати на платформах Node.js, браузері, 
Cordova, PhoneGap, Ionic, React Native, NativeScript, Expo та Electron, а також може 
використовуватись з TypeScript та JavaScript. Його мета – завжди підтримувати 
новітні функції JavaScript та надавати додаткові функції, які допоможуть вам 
розробляти будь-які програми, що використовують бази даних – від невеликих 
додатків з декількома таблицями до великомасштабних корпоративних додатків із 
декількома базами даних [20]. 
 
TypeORM зараз є найпопулярнішою бібліотекою ORM, створеною для проектів 
TypeScript. Він може працювати на багатьох платформах, зокрема: 
● Node.js 
● браузер 
● на мобільних пристроях - Cordova, PhoneGap, Ionic, React Native та NativeScript 
 ● Electron 
 
 
Бібліотека також підтримує шаблони Active Record та Data Mapper, що дозволяє 
розробникам створювати високоякісні, масштабовані та зручні в обслуговуванні 
програми, керовані базами даних. На нього сильно впливають інші ORM, такі як 
Hibernate, Doctrine та Entity Framework. Це означає, що розробники з досвідом 
роботи на Java та Ruby будуть почуватися як удома. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 45 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок - 3.2. – Команди для встановлення TypeORM. 
Переваги: 
● Добре працює як з TypeScript, так і з JavaScript; 
● Багато функцій і підтримує широкий спектр реляційних баз даних; 
● Функція конструктора запитів додає гнучкості та потужності, коли це 
важливо; 
 ● Ефективно використовує декоратори; 
● Функція міграції працює добре / є більш зрілою, ніж функція Prisma; 
 
Недоліки: 
● Погана документація для деяких випадків використання; 
● Не забезпечений значним фінансуванням; 
● Не підтримує поліморфні асоціації. 
(iii) Prisma 
Prisma — це ORM найновішого покоління для Node.js розробка якого закінчилась 
у 2021 році. Має всі потрібні інтсрументи для маніпулювання даним з бази даних 
[28]. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 46 
З мн. Арк. №  докум. Підпис Дата  
 
Prisma дуже відрізняється від інших ORM, які ми розглядали. Він використовує 
об'єктні моделі (класи сутностей), а скоріш файл схеми зіставлення всіх таблиць і 
шпальт. Цей файл використовується засобом міграції для створення файлу міграції 
SQL та бібліотекою клієнтів для створення визначень типів. Усі згенеровані 
визначення типів зберігаються в prisma/client/index.d.ts  файлі.  
 
Документація Prisma має гарний вигляд і, схоже, містить багато контенту. На жаль, 
мені було важко знайти потрібну інформацію. Або це через надто складну систему 
навігації, або через відсутність конкретного контенту. 
 
 
Рисунок - 2.3. – Команди для встановлення Prisma. 
 
 
 
Переваги: 
● Простий у використанні та інтуїтивно зрозумілий синтаксис 
● Чудово в стеку, який використовує GraphQL 
● Чудова підтримка TypeScript 
● Мова схем Prisma (PSL) є одночасно простим і потужним 
● За підтримки команди відданих розробників та венчурного фінансування 
 
Недоліки: 
● Багато функцій все ще розробляються 
● Функція міграції (Prisma Migrate) все ще є експериментальною 
● Немає довготривалих транзакцій 
● Немає доступу до базового підключення до бази даних 
● Неявна функція відношення багато-до-багатьох є трохи крихкою 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 47 
З мн. Арк. №  докум. Підпис Дата  
 
2.4.  Висновок 
Від технологій на яких написаний проект залежить майбутнє цього проекту адже 
саме вони забезбечують роботу проекту і якщо технології були вибрані не вдало 
під час розробки або під час життя проекту можуть виникати різні проблеми від 
простих до надскладних, що можуть мати негативні наслідки, наприклад, 
непередбачуваний збій і втрата клієнтів. А втрада клієнтів це втрата інвестицій а 
втрата інвестицій це майже кінець для проекту. Тому слід не аби як віднестися до 
вибору технлогій та не поскупитись найняти досвідченого експерта для цього. 
 
У другому розділу проведено вибір технологій за допомогою порівняня їх перевах 
та нелоліків. Складено таблиці з перевагами та недоліками для мов програмування 
(таблиця 2.1.), СУБД (таблиця 2.2.) та ORM (таблиця 2.3.). Проаналізувавши 
переваги та недоліки розглянутих виже технологій прийнято рішення 
використовувати JavaScript для Клієнтської частини (безпосередньо для самого 
 бота) та для Серверної частини так як ця мова гарантує найкращу швидкість для 
поставлених задач для боту. PostgreSQL – тому що вона має архітекту 
мультипроцесів та підходить для додатків з великим об’ємом як читання, так і 
запису. TypeORM – тому що це найпопулярніша ORM на сьогоднішній день, дуже 
добре підтримує ООП та TypeScript також підтримує шаблони Active Record і Data 
Mapper. 
Таблиця 2.1. – Переваги та недоліки мов програмування 
Мова Переваги Недоліки 
програмування 
JavaScript Популярний за Має тільки один потік 
(Node.js) статистикою останніх (thread) за замовчуванням. 
10-х років. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 48 
З мн. Арк. №  докум. Підпис Дата  
 
Написаний на V8 і 
завдяки цюому вона 
надшвидка. Підтримує 
як функціональне 
програмування так і 
ООП але більше 
спрямований на 
функціональне. 
Python Популярний за Повільна. Динамічна 
статистикою останніх типізація потребує більше 
5-х років. Підтримує як ресурсів. 
функціональне 
програмування так і 
ООП. 
 
Ruby Дуже малий поріг Повільна. Має відкритий 
входження. Підтримує код, будь хто може 
як функціональне модифікувати. Нове 
програмування так і покоління Back-end 
ООП але більше розробників не хочуть 
спрямований на ООП.  його вчити, тому що він 
застарілий. Проектів на 
цій мові все менше і 
менше. 
 
 
Таблиця 2.2. – Переваги та недоліки СУБД 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 49 
З мн. Арк. №  докум. Підпис Дата  
 
СУБД Переваги Недоліки 
PostgreSQL Мова SQL. Труднощі з оновленням 
Підходить для додатків даних. 
з великим об’ємом як 
читання, так і запису. 
Архітектура 
мультипроцесів. 
Oracle Database Мова SQL.  
MySQL Мова SQL. Не підходить для додатків 
з великим обсягом запису. 
Архітектура єдиного 
процесу. 
 MongoDB Дуже зручна панель NoSQL (логіку БД 
адміністратора. Дані не доведеться писати лише 
нормалізовані. на рівні додатку). 
 
 
 
Таблиця 2.3. – Переваги та недоліки ORM. 
Мова Переваги Недоліки 
Sequelize Достатньо популярний. Недостатня безпека, 
Велике ком'юніті. можна отримати дані 
за бази. Погано 
підтримує ООП та 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 50 
З мн. Арк. №  докум. Підпис Дата  
 
TypeScript. 
TypeORM Достатньо популярний. Не підтримує 
Велике ком'юніті. поліморфні асоціації. 
Добре підтримує ООП та 
TypeScript. 
Підтримує шаблони 
Active Record і Data 
Mapper, 
 
Prisma Дуже малий поріг Дуже нова, мале 
входження. Підтримує ком'юніті, потрібен час 
TypeScript. поки з'явиться 
ком'юніті. 
 
 
 
 
 
 3. РОЗРОБКА ПРОЕКТУ 
3.1. Стек та архітектура проекту 
Проект сладається з 4 частин: Серверна частина, Клієнтська частина 
(безпосередньо сам бот), база даних, DevOps. Серверна та Клієнтська частини 
побудовані на мові TypeScript. Мова TypeScript додає до JavaScript додатковий 
синтаксис для підтримки більш тісної інтеграції з вашим редактором [8]. 
Використання TypeScript дало можливість побудувати стабільне середовище. Все 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 51 
З мн. Арк. №  докум. Підпис Дата  
 
об'єднано у Docker [9] за допомогою Docker-compose [10] та розвернуто на 
хмарному хостингу DigitalOcean [12] за допомогою Docker-machine [11]. 
(i) Серверна частина 
Серверна частина побудована на Node.js з TypeScript. Для скрапінгу використані 
cheerio та iconv-lite бібліотеки.  
Cheerio — бібліотека node.js, вона зчитує розмітку сайту та надає API для 
маніпулювання структурою даних [13].  
Iconv-lite — бібліотека node.js, вона конвертує закодовані символи у читабельний 
для людини (utf8) текст [14]. 
Кешування розкладу пар відбувається за допомогою технології memcached.  
Memcached — Безкоштовна високопродуктивна система кешування об’єктів 
розподіленої пам’яті з відкритим кодом, загальна за своєю природою, але 
призначена для прискорення динамічних веб-додатків шляхом зменшення 
навантаження на базу даних [15]. 
 
(ii) Клієнтська частина 
Клієнтська частина (безпосередньо сам бот) побудована на Node.js з TypeScript. Для 
роботи з Telegram API викорисно бібліотеку node-telegram-bot-api.  
Node-telegram-bot-api — бібліотека node.js (SDK [16]) яка полегщує розробку боту 
для telegram. 
(iii) DevOps частина 
Все середовще по контейнерам об'єднано у Docker за допомогою Docker-compose 
та розвернуто на хмарному хостингу DigitalOcean за допомогою Docker-machine. 
 
Середовище має такі контейнери: 
● server (Серверна частина Node.js) 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 52 
З мн. Арк. №  докум. Підпис Дата  
 
● database (PostgreSQL) 
● cache (Memcached) 
● client (Клієнтська частина Node.js) 
 
3.2. Створення бази даних та таблиць 
Головна задача бота — це зберігання інформації необхідної для студентів, щоб 
отримувати розклад або для викладачів, щоб надслати повідомлення групам. 
Існуюча база даних була створена на реляційній моделі даних, використовуючи 
обрану СУБД вище PostgreSQL. Для управління маніпулювання базою даних 
використано node.js бібліотеку pg [21] та обрану вище ORM — TypeORM. 
 
База містить у собі дві необхідні таблиці (рис. 3.1.): 
● users 
● groups 
 
 
Рисунок 3.1. – Структура бази даних. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 53 
З мн. Арк. №  докум. Підпис Дата  
 
(i) Таблиця користувачів 
 
Таблиця користувачів (users) містить такі колонки (рис 3.2.): 
● id – Id користувача у системі бота (індексована (unique, pk), auto-increment 
колонка тип якої number, не потрібно передавати при створенні моделі user, 
тому що вона створюється автоматично базою даних); 
● chatId – Унікальне id Telegram чату яке залежить від id Telegram користувача, 
якщо це приватна група (індексована (unique) колонка тип якої string, являється 
обов'язковою при створенні моделі user); 
● username – Нікнейм, який записано в Telegram (неіндексована колонка тип якої 
string, являється не обов'язковою при створенні моделі user); 
● firstName – Ім'я, яке записано в Telegram (неіндексована колонка тип якої string 
являється не обов'язковою при створенні моделі user); 
● title – Назва групи яке записано в Telegram, якщо груповий чат (неіндексована 
 колонка тип якої string, являється не обов'язковою при створенні моделі user); 
● groupId – Іd доданої користувачем групи (індексована колонка тип якої string, 
являється не обов'язковою при створенні моделі user); 
● role – Роль користувача боту (неіндексована колонка тип якої enum EUserRole 
(рис 3.3.), являється обов'язковою при створенні моделі user, значення по 
замовчуванню DEFAULT, тобто 1); 
● type – Роль користувача боту (неіндексована колонка тип якої enum EUserType 
(рис 3.4.), являється обов'язковою при створенні моделі user); 
● createdAt – Дата та час, коли був створений користувач боту (неіндексована 
колонка тип якої number, являється не обов'язковою при створенні моделі user, 
тому що створюється автоматино базою даних). 
● updatedAt – Дата та час, коли останній раз був оновлений користувач боту 
(неіндексована колонка тип якої number, являється не обов'язковою при 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 54 
З мн. Арк. №  докум. Підпис Дата  
 
створенні моделі user, тому що створюється автоматино базою даних, 
оновлюється автоматично базою даних). 
● deletedAt – Дата та час, коли був видалений користувач боту (неіндексована 
колонка тип якої number, не передається при створенні моделі user, тому щоза 
допомогою цієї колонки позначається видалений користувач чи ні, це метод 
видалення називається Soft Delete [22]). 
 
Реляційний зв'язок з таблицею groups: many-to-one (користувачі можуть мати лише 
одну групу). Зв'язок будується за допомогую колонки groupId. 
 
Код моделі представлений у Додатку А. 
 
 
 
Рисунок 3.2. – Структура моделі користувача з її індексами. 
 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 55 
З мн. Арк. №  докум. Підпис Дата  
 
                        
 
Рисунок 3.3. – Enum тип EUserRole.                            Рисунок 3.4. – Enum тип EUserType. 
(ii) Таблиця груп 
 
Таблиця груп (groups) містить такі колонки (рис 3.5.): 
● id – Id групи у системі бота (індексована (unique, pk), auto-increment колонка тип 
якої number, не потрібно передавати при створенні моделі group, тому що вона 
створюється автоматично базою даних); 
● name – Унікальна, форматована назва групи університету (індексована (unique) 
 
колонка тип якої string, являється обов'язковою при створенні моделі group); 
● realName – Унікальна, не форматована назва групи університету (індексована 
(unique) колонка тип якої string, являється обов'язковою при створенні моделі 
group); 
● createdAt – Дата та час, коли був створений користувач боту (неіндексована 
колонка тип якої number, являється не обов'язковою при створенні моделі group, 
тому що створюється автоматино базою даних). 
 
Реляційний зв'язок з таблицею users: one-to-many (группа може мати багато 
користувачів). Зв'язок будується за допомогую колонки groupId у таблиці users. 
 
Код моделі представлений у Додатку А. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 56 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.5. – Структура моделі групи з її індексами. 
 
 
 
 
3.3. Створення серверної частини 
Існуюча серверна частина була створена на основі обраної мови програмування 
JavaScript та фреймворку express і TypeORM. 
 
 
Express — це мінімальний і гнучкий фреймворк веб-додатків Node.js, який надає 
надійний набір функцій для веб- та мобільних додатків. Express надає тонкий шар 
основних функцій веб-додатків, не приховуючи функції Node.js [23]. 
(i) Моделі 
Концепція моделі (entity) в TypeORM майже не відрізняється від базового 
означення цього компоненту в MVC. Модель — це складова патерну проектування 
MVC, що відповідає роботу з базою даних та надає зручний інтерфейс доступу до 
них. В базовому додатку написаному на TypeORM можна зробити так щоб кожна 
модель (ооп клас) наслідувалася від BaseEntity класу. Після цього наслідування при 
роботі з моделлю можна використовувати допоміжні методи та інструменти, що 
значно спрощують розробку будь-якого проекту (додаються методи за допомогою 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 57 
З мн. Арк. №  докум. Підпис Дата  
 
TypeScript докораторів для валідації, роботи з помилками, витягання з бази 
необхідної інформації моделі тощо). TypeORM автоматично робить міграції бази 
данних коли оновлюються фали моделей. 
Детальний код створення моделей представлений у Додатку А. 
 
 
 
Рисунок 3.6. – Приклад створення моделі у TypeORM (TypeSript). 
(ii) Контролери 
Контролери в TypeORM беруть свій початок від базового визначення. Контролер – 
це компонент патерну MVC, що забезпечує зв’язок між користувачами та системою 
а саме контролює введення даних користувачами та використовує моделі та 
представлення для реалізації необхідної реакції. Всі контролери у проекті 
наслідуються від власноруч створеного базового контролера BaseController (рис 
3.8.), що містить методи, для відправки REST відповідей на запити, та логування 
помилок, ці методи доступні для всіх контролерів додатку які наслідуються від 
BaseController. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 58 
З мн. Арк. №  докум. Підпис Дата  
 
Між контролерами та сервісами є прямий зв’язок. При розробці контролерів 
додатку на кожний метод контролера є мінімум один метод сервіса, який виконує 
ту чи іншу роботу.   
 
Детальний код створення моделей представлений у Додатку А. 
 
 
 
Рисунок 3.7. – Приклад власноруч створеного BaseController. 
(iii) Сервіси 
Сервіси в додатку відіграють свою важливу роль роблячи код більш зрозумілим 
саме завдяки сервісам контролери не громосткі і виглядають читабельно. У кожної 
моделі є сервіс. Контролер не може на пряму пряцювати з базою даних, це робиться 
виключно через сервіс, котролер викликає методи сервісів у середині своїх методів. 
Таким чином ми  маємо інкапсуляцію. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 59 
З мн. Арк. №  докум. Підпис Дата  
 
 
Детальний код створення сервiсiв представлений у Додатку А. 
(iv) Маршрутизаця 
Метою маршрутизації (роутинга) в будь-якому Express додатку є зв’язування 
вхідних url з конкретною дією контролера. Роутер в Express розпізнає url 
посилання, з’єднує запит користувача з необхідним контролером та віддає 
результат обробки запиту назад на Front-end. Також маршрутизатор 
використовується для генерації шляхів (маршрутів) в додатку без необхідності явно 
прописувати кожен маршрут. Для цього використовуються спеціальні структури, 
які розуміло та ефективно описують блоки маршрутів. 
 
Детальний код маршрутизації контролерів представлений у Додатку А. 
 
Таблиця 3.1. – структура маршрутизації users контролеру 
 
Url та Метод Опис 
GET /users Отримання всіх зареєстрованих користувачів 
GET /users/:chatId Отримання одного користувача за його chatId 
POST /users Створення нового користувача 
PUT /users/:chatId Оновлення існуючого користувача за його chatId 
DELETE Видалення існуючого користувача за його chatId 
/users/:chatId 
 
Таблиця 3.2. – структура маршрутизації groups контролеру 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 60 
З мн. Арк. №  докум. Підпис Дата  
 
Url та Метод Опис 
GET /groups Отримання всіх груп університету 
GET /groups/names Отримання всіх назв груп університету 
POST /groups Створення групи університету 
 
Таблиця 3.2. – структура маршрутизації schedule контролеру 
Url та Метод Опис 
GET /schedule Отримання розкладу 
(v) Файлова структура 
● src/  
○ config/ 
 
■ axios.config.ts 
■ database.config.ts 
■ memcached.config.ts 
○ constants/ 
■ config.constants.ts 
■ errors.constants.ts 
■ index.ts fixes 
■ request.constants.ts 
■ schedule.constants.ts 
○ modules/ 
■ group/ 
● group.controller.ts 
● group.entity.ts 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 61 
З мн. Арк. №  докум. Підпис Дата  
 
● group.service.ts 
■ schedule/ 
● schedule.controller.ts 
● schedule.dto.ts 
● schedule.service.ts 
■ user/ 
● user.controller.ts 
● user.dto.ts 
● user.entity.ts 
● user.service.ts 
● user.typedef.ts 
■ base.controller.ts 
○ scripts/ 
■ group.script.ts 
 ○ utils/ 
■ cache.util.ts 
■ converter.util.ts 
■ index.ts 
■ logger.util.ts 
■ parser.util.ts 
■ request.util.ts 
○ index.ts 
○ server.ts 
● Dockerfile 
● package.json 
● tsconfig.json 
● yarn.lock 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 62 
З мн. Арк. №  докум. Підпис Дата  
 
3.3. Створення клієнтської частини 
Існуюча клієнтська частина була створена на основі обраної мови програмування 
JavaScript та фреймворку express і бiбліотеки node-telegram-bot-api. 
 
Node-telegram-bot-api — це node.js бібліотека для полегшення роботи з телеграм 
API [5] містить у собі купу інтрументів для комунікації з ним [6]. 
 
Спочатку треба було зареєструвати бота в телеграм у @BotFather. Отримати токен 
для використання зареєстрованого бота. Токен потрібен для роботи з Telegram API 
[5].  
(i) Ініціювання боту 
Після додавання боту у приватний чат або у груповий чат його потрібно ініціювати, 
щоб він почам стежити за повідомленнями, у більшості випадків такі повідомлення 
 починаются з “/” (слешу). Він не слідкує абсолютно за всіма повідомленнями він 
чакеє на повідомлення, які у ньому запрограмовані. 
 
Після ініціювання боту будут доступні команди та спеціальна клавіатура (рис. 
3.8., рис 3.9.): 
● /start – команда, що ініціює бота; 
● /group – команда для додавання/оновлення групи; 
● /today – команда для отримання розкладу на сьогодні; 
● /tomorrow – команда для отримання розкладу на завтра; 
● /week – команда для отримання розкладу на тиждень; 
● /clear – команда для видалення групи та користувача; 
● /permissions – команда для запиту прав адміністратора; 
● /pm – команда адміністратора для відправки повідомлень групам; 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 63 
З мн. Арк. №  докум. Підпис Дата  
 
● /users – команда адміністратора для отримання всіх зареєстрованих 
користувачів. 
 
 
Рисунок 3.8. – Приклад спеціальної клавіатури на web версії Telegram. 
 
 
Рисунок 3.9. – Приклад спеціальної клавіатури у моб. додатку Telegram. 
(ii) Реєстрація групи 
Для реєстрації групи потрібно знати її назву. Зареєструвати групу можна за тією ж 
командою що і додавати: “/group” Приклад реєстрації групи зображено на  рис 3.10. 
Якщо введена не  правильна назва групи – бот поверне відповідне повідомлення 
(рис 3.11.). 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 64 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.10. – Приклад реєстрації групи. 
 
 
Рисунок 3.11. – Приклад введення не існуючої групи. 
  
(iii) Оновлення групи 
Для оновлення групи потрібно знати назву нової групи. Оновити групу групу 
можна за тією ж командою що і додавати: “/group”. Регістер не впливає на 
результат, бот однаково опрацює наступні дві назви групи: web1811+ск, 
WEB1811+СК. Приклад оновлення групи зображено на  рис 3.12. Якщо введена не  
правильна назва групи – бот поверне відповідне повідомлення праклад 
представлений виже на рис. 3.13. Якщо введена група вже додана – бот поверне 
відповідне повіомлення (рис. 3.11.). 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 65 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.12. – Приклад оновлення групи. 
 
 
Рисунок 3.13. – Приклад введення вже доданої групи. 
 (iv) Видалення користувача 
Кожен користувач має можливість видалити свою групу та свію запис у базі 
даних, щоб більше не отримувати повідомлення. Видалити групу можна за 
допомогою команди “/clear”, вводити нозву групи вже не потрібно, тому що вона 
закріплена за користувачем і система боту її знає. Приклад видалення групи 
зображено на рис. 3.14. 
Якщо користувач вводе команду але ше не має доданої групи – бот поверне 
повідомлення про це (рис. 3.15.). 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 66 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.14. – Приклад видалення групи. 
 
 
Рисунок 3.15. – Приклад видалення групи, коли вона ще не додана. 
 (v) Отримання розкладу 
Отримання розкладу може відбуватися двома шляхами: 
● набрати команду в ручну; 
● скористатися спеціальною клавіатурою, якщо користуватися цим шляхом то 
отримання розкладу буде всього за 4 кліки:  
a. Перший. Зайти в Telegram; 
b. Другий. Зайти в чат або групу з ботом; 
c. Третій. Відкрити спеціальну клавіатуру; 
d. Четвертий. Обрати період (періоди представленні нижче); 
 
Розклад можна отримати на наступні періоди: 
● На сьогодні. Команда /today (рис. 3.16.); 
● На завтра. Команда /tomorrow (рис. 3.17.); 
● На тиждень. Команда /week (рис. 3.18. - 4.21.); 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 67 
З мн. Арк. №  докум. Підпис Дата  
 
 
Отриманий розклад можна перевірити на сайті ЧДТУ. Бот повертає розклад з 
підкріпленою гіпер-силкою “Перевірити розклад” на сайт ЧДТУ з заповненою 
формою. 
 
Ящко не має розкладу на вибраний період – бот поверне повідомлення про те що 
не знайдено пар (рис. 4.22.). 
 
 
 
Рисунок 3.16. – Приклад розкладу на сьогодні. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 68 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.17. – Приклад розкладу на завтра. 
 
 
Рисунок 3.18. – Приклад розкладу на тиждень. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 69 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.19. – Приклад розкладу на тиждень (доповнення рис. 3. 18.) 
 
 
Рисунок 3.20. – Приклад розкладу на тиждень (доповнення рис. 3. 18.) 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 70 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.21. – Приклад розкладу на тиждень (доповнення рис. 4. 18.) 
 
 
 
Рисунок 3.22. – Приклад розкладу коли немає пар 
(vi) Відправка повідомлень групам 
Відправка повідомлень групам може бути здійснена лише адміністратором. Для 
відправки повідомлення використовується команда: “/pm <receiver>”. Бот 
повністю копіює введене адміном повідомлення після сигналу та відправляє 
отримувачу(ам). Коли повідомлення надіслане бот повідомляє проц е 
адміністратора (рис 3.23.) 
 
<receiver> - realGroupName або groupName або chatId отримувача(ів). 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 7 1 
З мн. Арк. №  докум. Підпис Дата 
 
 
Рисунок 3.23. – Приклад відправки повідомлень користувачам (стедентам) 
(vii) Відправка повідомлень адміністратору 
Коли реєструються нові користувачі бот присилає повідомлення адміністратору 
(рис. 3.24.). Це зроблено для того щоб мати можливість проаналізувати кількість 
нових користувачів та з яких груп та кафедри студенти реєструються частіше. 
 
 
 
Рисунок 3.24. – Приклад повідомлення при створенні нової групи 
(viii) Маніпулювання ботом 
За допомогою команди “/users” бот повертає адміну всих зареєстрованиї 
користувачів разом з групами, що вони додали (рис 3.25.). 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 72 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.25. – Приклад повідомлення при створенні нової групи 
Якщо у користувача немає прав адміністратора і він хоче скористуватися 
командами: “/pm” та “/users” – бот надішле йому помилку (рис. 3.26.). 
 
 
 
Рисунок 3.26. – Приклад відмови у доступі 
 
Якщо користувач хоче мати права адміністратора потрібно відправити запит з 
проханням отримати доступ та описати навіщо користувачеві права адміністратора 
(рис. 3.27.). Його запит буде надіслано всим адміністратоам на розглядання. 
Команда для відправки запиту: “/permissions”. 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 73 
З мн. Арк. №  докум. Підпис Дата  
 
 
Рисунок 3.27. – Приклад запиту на права адміністратора 
(ix) Повідомлення про збій 
Користувач повинен знати коли щось пішло не так, як заплановано – бот інформує 
користувача і паралельно адміністраторів (рис 3.28.), якщо в роботі бота сталось 
щось непередбачуване. 
 
 
 
Рисунок 3.28. – Приклад повідомлення про збій у роботі бота 
(x) Файлова структура 
● src/  
○ config/ 
■ index.ts 
■ request.config.ts 
○ constants/ 
■ bot.constants.ts 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 74 
З мн. Арк. №  докум. Підпис Дата  
 
■ config.constants.ts 
■ error.constants.ts 
■ index.ts 
■ message.constants.ts 
○ services/ 
■ api.service.ts 
■ bot.service.ts 
■ exeption.service.ts 
■ index.ts 
■ scedule.service.ts 
○ client.ts 
○ index.ts 
● Dockerfile 
● package.json 
 ● tsconfig.json 
● yarn.lock 
3.4. Висновок 
 
У третьому розділі представлений стек проекту то описано розробку серверної та 
кліентської частин. Наведено приклади роботи сестеми у цілому. 
 
Готовий проект надає студентам та викладачам наступні можливості: 
● Реєстрація групи. Групу не потрібно додавати кожен раз, вона зберігається 
поки користувач її не змінить або видалить, від вибраної (зареєстрованої) 
групи буде залежати розклад; 
● Оновлення групи; 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 75 
З мн. Арк. №  докум. Підпис Дата  
 
● Видалення групи; 
● Отримання розкладу на сьогодні, завтра та  тиждень (7 днів); 
● Перевірка розкладу на офіціальному сайті за посиланням; 
● Відправку повідомлень, оголошень групі(ам). Таким чином всим студентам у 
кого зареєстрована група може бути надіслано повідомлення, оголошення; 
● Запит на права адміністратора; 
 
Були усунуті наступні недоліки аналогів: 
● Читабельність веділення основної інформаціі та групування тексту. Кожен 
день проходе як кореме повідомлення. 
● Додано можливість перевірити розклад, натиснувши на кнопку перевірити 
розклад відкривається офіційна веб-сторінка з заповноною формою та вже 
отриманим розкладом. 
● Покраженна швідкість отримання розкладу за допомогою кешування запитів. 
 Швидкість отримання розкладу досягла результату в 10-30  мілісекунд, що 
доволі швидко. 
● Всі данні користувача шифруються у базі данних, таким чином, при взломі, 
у хакерів не буде персональної інформації студентів а саме ім'я, яке показує 
телеграм та нікнейму, яке показує телеграм. 
● Розклал можна отримати за 20 секунд у 4 кліки з моменту розблокування 
телефону. 
 
 
 
 
 
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 76 
З мн. Арк. №  докум. Підпис Дата  
 
 
 
 
 
 
 
 
 
 
 
 
 
ВИКОРИСТАНІ ДЖЕРЕЛА 
1. GlobalWebIndex [Електронний ресурс]. Режим доступу – 
 https://blog.gwi.com/trends/3-reasons-the-chat-bot-revolution-will-work/. Дата 
звернення – 23.04.2022. 
2. Quantcast [Електронний ресурс]. Режим доступу – 
https://www.quantcast.com/. 
Дата звернення – 23.04.2022. 
3. VoIP [Електронний ресурс]. Режим доступу – 
https://uk.wikipedia.org/wiki/VoIP#:~:text=VoIP%20(%D0%B0%D0%BD%D0
%B3%D0%BB.,%D0%B4%D0%BE%D0%BF%D0%BE%D0%BC%D0%BE%
D0%B3%D0%BE%D1%8E%20%D1%81%D1%96%D0%BC%D0%B5%D0%B
9%D1%81%D1%82%D0%B2%D0%B0%20%D0%BF%D1%80%D0%BE%D1
%82%D0%BE%D0%BA%D0%BE%D0%BB%D1%96%D0%B2%20TCP%2FI
P. Дата звернення – 23.04.2022. 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 77 
З мн. Арк. №  докум. Підпис Дата  
 
4. Telegram org [Електронний ресурс]. Режим доступу – https://telegram.org/. 
Дата звернення – 25.04.2022.  
5. Telegram Bot API doc [Електронний ресурс]. Режим доступу – 
https://core.telegram.org/bots/api. Дата звернення – 23.04.2022.  
6. Node Telegram Bot Api [Електронний ресурс]. Режим доступу – 
https://github.com/yagop/node-telegram-bot-api#readme. Дата звернення – 
23.04.2022.  
7. Viber Bot API doc [Електронний ресурс]. Режим доступу – 
https://developers.viber.com/docs/api/rest-bot-api/. Дата звернення – 
23.04.2022. 
8. TypeScript doc[Електронний ресурс]. Режим доступу – 
https://www.typescriptlang.org/. Дата звернення – 23.04.2022.  
9. Docker [Електронний ресурс]. Режим доступу – https://docker.com/.  Дата 
звернення – 23.05.2022.  
 10. Docker Compose [Електронний ресурс]. Режим доступу – 
https://docs.docker.com/compose/. Дата звернення – 23.04.2022.  
11. Docker Machine [Електронний ресурс]. Режим доступу – 
https://docs.docker.com/machine/. Дата звернення – 23.04.2022.  
12. DigitalOcean [Електронний ресурс]. Режим доступу – 
https://www.digitalocean.com/. Дата звернення – 23.04.2022.  
13. Cheereio org [Електронний ресурс]. Режим доступу – https://cheerio.js.org/. 
Дата звернення – 23.04.2022.  
14. Iconv Lite [Електронний ресурс]. Режим доступу – 
https://github.com/ashtuchkin/iconv-lite#readme/. Дата звернення – 23.04.2022.  
15. Memcached [Електронний ресурс]. Режим доступу – https://memcached.org/. 
Дата звернення – 23.04.2022.  
Арк. 
ЧДТУ 22WEB-1805 ПЗ 78 
З мн. Арк. №  докум. Підпис Дата  
 
16. SDK [Електронний ресурс]. Режим доступу – 
https://www.redhat.com/en/topics/cloud-native-apps/what-is-
SDK#:~:text=A%20software%20development%20kit%20(SDK,OS)%2C%20or
%20programming%20language/. Дата звернення – 23.04.2022.  
17. Мова програмування [Електронний ресурс]. Режим доступу – 
https://uk.wikipedia.org/wiki/%D0%9C%D0%BE%D0%B2%D0%B0_%D0%BF
%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D1%83%D0%B2
%D0%B0%D0%BD%D0%BD%D1%8F. Дата звернення – 23.04.2022.  
18. ORM [Електронний ресурс]. Режим доступу – 
https://uk.theastrologypage.com/object-relational-mapping/. Дата звернення – 
23.04.2022.  
19. Sequelize [Електронний ресурс]. Режим доступу – 
https://habr.com/ru/post/565062/. Дата звернення – 23.04.2022.  
20. TypeORM[Електронний ресурс]. Режим доступу – 
 https://bestprogrammer.ru/programmirovanie-i-razrabotka/9-luchshih-orm-dlya-
javascript-i-typescript-na-2021-god/. Дата звернення – 23.04.2022.  
21. Node module PG [Електронний ресурс]. Режим доступу – https://node-
postgres.com/. Дата звернення – 23.04.2022.  
22. Soft Delete[Електронний ресурс]. Режим доступу – 
https://en.wiktionary.org/wiki/soft_deletion/. Дата звернення – 23.04.2022.  
23. Express [Електронний ресурс]. Режим доступу – https://expressjs.com/. Дата 
звернення – 23.04.2022.  
24. PostgreSQL [Електронний ресурс]. Режим доступу – 
https://www.postgresql.org/. Дата звернення – 23.04.2022.  
25. OracleDatabase [Електронний ресурс]. Режим доступу – 
https://www.oracle.com/database/. Дата звернення – 23.04.2022.  
Арк. 
ЧДТУ 22WEB-1805 ПЗ 79 
З мн. Арк. №  докум. Підпис Дата  
 
26. MySQL [Електронний ресурс]. Режим доступу – https://www.mysql.com/. 
Дата звернення – 23.04.2022.  
27. MongoDB [Електронний ресурс]. Режим доступу – 
https://www.mongodb.com/cloud/atlas/lp/try2?utm_content=rlsavisitor&utm_sou
rce=google&utm_campaign=gs_emea_rlsamulti_search_core_brand_atlas_deskto
p_rlsa&utm_term=mongodb&utm_medium=cpc_paid_search&utm_ad=e&utm_a
d_campaign_id=14412646455&adgroup=131761126492&gclid=Cj0KCQjwm6K
UBhC3ARIsACIwxBg9__GQ7l3eRiQxKEJiqEQJ5mbxiNUk6W-
u8qr6L1H5PVdQpKfVVrsaAggJEALw_wcB. Дата звернення – 23.04.2022.  
28. Prisma [Електронний ресурс]. Режим доступу – https://www.prisma.io/. Дата 
звернення – 23.04.2022.  
29. СУБД [Електронний ресурс]. Режим доступу – https://itglobal.com/ru-
ru/company/glossary/subd-sistema-upravleniya-bazami-dannyh/. Дата звернення 
– 23.04.2022.  
 30. Node.js [Електронний ресурс]. Режим доступу – https://nodejs.org/en/. Дата 
звернення – 23.04.2022.  
31. JavaScript повний посібник; довідник з найпопулярнішої мови 
програмування, 7-е вид. Фленаган Д. 
32. Вивчаємо JavaScript: посібник із створення сучасних веб-сайтів. 3-тє 
видання. Браун Е. 
33. JavaScript с нуля. Чиннатхамби К. 
34. Node.js у дії Кантелон М.Хартер М.Головайчук T.Райліх Н. 
35. Вивчаємо Node.js Пауерс Ш. 
36. Основы программирования на Java, ООП, алгоритмы и структуры данных. 
Седжвик Р., Уэйн К.  
37. Пошуковий бот Google. [Електронний ресурс]. Режим доступу – 
https://uk.wikipedia.org/wiki/Googlebot#:~:text=Googlebot%20%E2%80%94%2
Арк. 
ЧДТУ 22WEB-1805 ПЗ 80 
З мн. Арк. №  докум. Підпис Дата  
 
0%D1%86%D0%B5%20%D0%BF%D0%BE%D1%88%D1%83%D0%BA%D0
%BE%D0%B2%D0%B8%D0%B9%20%D1%80%D0%BE%D0%B1%D0%BE
%D1%82%2C%20%D1%81%D1%82%D0%B2%D0%BE%D1%80%D0%B5%
D0%BD%D0%B8%D0%B9,txt%20%D0%B0%D0%B1%D0%BE%20%D0%B
C%D0%B5%D1%82%D0%B0%D1%82%D0%B5%D0%B3%20noindex. Дата 
звернення – 27.04.2022. 
38. SSL Certificate [Електронний ресурс]. Режим доступу – 
https://www.digicert.com/what-is-an-ssl-certificate/. Дата звернення – 
27.04.2022. 
ПРОГРАМНІ ЗАСОБИ 
● Microsoft 365 © Microsoft Inc., 2022. 
● WebStorm © JetBrains s.r.o., 2022.
 
Арк. 
ЧДТУ 22WEB-1805 ПЗ 81 
З мн. Арк. №  докум. Підпис Дата  
 
ДОДАТОК A 
 
ЗАТВЕРДЖЕНО 
Зав. кафедри ІТП, проф. 
_________________ Прокопенко Т.О. 
«____» ________________ 2022 р. 
 
 
РОЗРОБКА ТЕЛЕГРАМ БОТУ ДЛЯ ПІДТРИМКИ ОСВІТНЬОГО ПРОЦЕСУ 
ЗАКЛАДУ ВИЩОЇ ОСВІТИ 
 
 
Код програми 
482 ЧДТУ 22WEB-1805-01 
Листів 33 
 
Розробник ____________________ Зінченко М. Р. 
Керівник ____________________ Прокопенко Т.О. 
Н. Контроль ____________________  
 
 
 
 
 
Черкаси – 2022 року
 
 
Лістинг файлів серверної частини 
 
Лістинг файлу: /sercer/Dockerfile 
 
FROM node:14 as base 
 
ENV SERVER_ROOT /server 
 
RUN mkdir $SERVER_ROOT 
WORKDIR $SERVER_ROOT 
 
COPY ./package.json ./ 
 
RUN yarn 
 
COPY . $SERVER_ROOT 
 
FROM base as production 
 
RUN yarn build 
 
Лістинг файлу: /server/package.json 
 
{ 
 "name": "schedule-bot-server", 
 "version": "0.0.1", 
 "description": "", 
 "author": { 
   "name": "Max Zinchenko", 
   "url": "https://github.com/maxzinchenko" 
 }, 
 "keywords": [ 
   "Cherkasy State Technological University" 
 ], 
 "license": "MIT", 
 "main": "dist/index.js", 
 "engines": { 
   "node": ">=14 <15" 
 }, 
 "scripts": { 
   "typeorm": "node --require ts-node/register ./node_modules/.bin/typeorm", 
   "start:dev": "cross-env NODE_ENV=development ./node_modules/.bin/nodemon 
src/index.ts --watch", 
   "start:prod": "cross-env NODE_ENV=production node dist/index.js", 
   "group:script:dev": "cross-env NODE_ENV=development ts-node 
src/scripts/group.script.ts", 
   "group:script:prod": "cross-env NODE_ENV=production node 
dist/scripts/group.script.js", 
   "build": "rm -rf dist && cross-env NODE_ENV=production tsc" 
 }, 
 "dependencies": { 
   "axios": "0.21.1", 
   "cheerio": "1.0.0-rc.5", 
   "class-transformer": "^0.4.0", 
   "class-validator": "0.13.1", 
   "dotenv": "8.2.0", 
2 
 
   "express": "4.17.1", 
   "form-data": "4.0.0", 
   "iconv-lite": "0.6.2", 
   "memcached": "2.2.2", 
   "pg": "8.5.1", 
   "reflect-metadata": "^0.1.13", 
   "routing-controllers": "^0.9.0", 
   "typeorm": "0.2.30" 
 }, 
 "devDependencies": { 
   "@types/express": "4.17.11", 
   "@types/memcached": "2.2.6", 
   "@types/node": "14.14.22", 
   "cross-env": "7.0.3", 
   "nodemon": "2.0.7", 
   "ts-node": "9.1.1", 
   "typescript": "4.2.2" 
 } 
} 
 
Лістинг файлу: /server/src/config/axios.config.ts 
 
import axios from 'axios'; 
import iconv from 'iconv-lite'; 
 
axios.interceptors.response.use(res => { 
 res.data = iconv.decode(Buffer.from(res.data, 'binary'), 'win1251'); 
 
 return res; 
}); 
 
export { axios }; 
 
Лістинг файлу: /server/src/config/database.config.ts 
 
import { createConnection as connect, EntitySchema } from 'typeorm'; 
 
import { DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_USER, ENV } from 
'../constants'; 
 
const createConnection = (entities: Function[] | EntitySchema[]) => connect({ 
 type: 'postgres', 
 host: DB_HOST, 
 port:  DB_PORT, 
 username: DB_USER, 
 password: DB_PASSWORD, 
 database: `${ DB_NAME }_${ ENV }`, 
 entities, 
 synchronize: true, 
 logging: true 
}); 
 
export const database = { createConnection }; 
 
Лістинг файлу: /server/src/config/memcached.config.ts 
 
3 
 
import Memcached from 'memcached'; 
 
import { MEMCACHED_ADDRESS } from '../constants'; 
 
export const memcached = new Memcached(MEMCACHED_ADDRESS, { retries: 10, retry: 
10000, remove: true }); 
 
Лістинг файлу: /sercer/constants/config.constants.ts 
 
export const SERVER_PORT = parseInt(process.env.SERVER_PORT!); 
 
export const ENV = process.env.NODE_ENV; 
export const PRODUCTION = ENV === 'production'; 
 
export const DB_NAME = process.env.POSTGRES_DB; 
export const DB_PORT = parseInt(process.env.POSTGRES_PORT!); 
export const DB_HOST = process.env.POSTGRES_HOST; 
export const DB_USER = process.env.POSTGRES_USER; 
export const DB_PASSWORD = process.env.POSTGRES_PASSWORD; 
 
export const MEMCACHED_ADDRESS = process.env.MEMCACHED_ADDRESS; 
 
export const API_URL = process.env.API_URL; 
export const API_USER_AGENT = process.env.API_USER_AGENT; 
 
Лістинг файлу: /sercer/constants/errors.constants.ts 
 
export const GROUP_NOT_FOUND = 'GROUP_NOT_FOUND'; 
export const GROUP_INVALID = 'GROUP_INVALID'; 
 
export const USER_NOT_FOUND = 'USER_NOT_FOUND'; 
export const USER_INVALID = 'USER_INVALID'; 
export const USER_GROUP_NOT_FOUND = 'USER_GROUP_NOT_FOUND'; 
 
export const FORBIDDEN = 'FORBIDDEN'; 
 
Лістинг файлу: /sercer/constants/index.ts 
 
export * from './config.constants'; 
export * from './schedule.constants'; 
export * from './request.constants'; 
export * from './errors.constants'; 
 
Лістинг файлу: /sercer/constants/request.constants.ts 
 
export const REQUEST_TYPE = { 
 schedule: 'schedule' as 'schedule', 
 groups: 'groups' as 'groups', 
 teachers: 'teachers' as 'teachers' 
} 
 
export const REQUEST_N = { 
 schedule: 700 as 700, 
 groups: 701 as 701, 
4 
 
 teachers: 701 as 701 
}; 
 
export const REQUEST_DATA = { 
 schedule: { faculty: 0, teacher: '', group: '', sdate: '', edate: '', n: 700 }, 
 groups: { faculty: 0, lev: 142, n: 701 }, 
 teachers: { faculty: 0, lev: 141, n: 701 } 
}; 
 
Лістинг файлу: /sercer/constants/schedule.constants.ts 
 
export const SCHEDULE_TIME_TABLE = [{ 
 startAt: '8:30', 
 endAt: '9:50' 
}, { 
 startAt: '10:00', 
 endAt: '11:20' 
}, { 
 startAt: '11:50', 
 endAt: '13:10' 
}, { 
 startAt: '13:20', 
 endAt: '14:40' 
}, { 
 startAt: '14:50', 
 endAt: '16:10' 
}, { 
 startAt: '16:20', 
 endAt: '17:40' 
}, { 
 startAt: '17:50', 
 endAt: '19:10' 
}, { 
 startAt: '19:20', 
 endAt: '20:40' 
}, { 
 startAt: '20:50', 
 endAt: '22:10' 
}]; 
 
Лістинг файлу: /server/src/server.ts 
 
import { Application, RequestHandler } from 'express'; 
import http from 'http'; 
import { Connection, EntitySchema } from 'typeorm'; 
import { useExpressServer } from 'routing-controllers'; 
 
import { PRODUCTION } from './constants'; 
import { database } from './config/database.config'; 
 
const EXT = PRODUCTION ? 'js' : 'ts'; 
 
export class Server { 
 constructor(private readonly app: Application, private readonly port?: number) { 
   this.app = app; 
   this.port = port; 
 } 
5 
 
 
 public run(): http.Server { 
   return this.app.listen(this.port, () => { 
     console.log(`\nApi is up and running on port ${ this.port }\n`) 
   }); 
 } 
 
 public loadMiddleware(middleware: Array<RequestHandler>): void { 
   middleware.forEach(mw => { 
     this.app.use(mw); 
   }); 
 }; 
 
 public loadControllers(): void { 
   useExpressServer(this.app, { 
     routePrefix: '/api/v1', 
     controllers: [`${__dirname}/**/*.controller.${ EXT }`] 
   }); 
 }; 
 
 public initDatabase(entities: Function[] | EntitySchema[]): Promise<Connection> { 
   return database.createConnection(entities) 
 } 
} 
 
Лістинг файлу: /server/src/index.ts 
 
import 'dotenv/config'; 
import 'reflect-metadata'; 
import express, { RequestHandler } from 'express'; 
import { urlencoded, json } from 'body-parser' 
 
import { SERVER_PORT } from './constants' 
import { Server } from './server'; 
 
import { User } from './modules/user/user.entity'; 
import { Group } from './modules/group/group.entity'; 
 
const app = express(); 
const server = new Server(app, SERVER_PORT); 
 
const globalMiddleware: Array<RequestHandler> = [ 
 urlencoded({ extended: false }), 
 json() 
]; 
 
server.initDatabase([User, Group]) 
 .then(() => { 
   server.loadMiddleware(globalMiddleware); 
   server.loadControllers(); 
   server.run(); 
 }) 
 .catch(console.error); 
 
Лістинг файлу: /server/src/utils/cache.util.ts 
 
import { memcached } from '../config/memcached.config'; 
6 
 
import { logger } from './logger.util'; 
 
const LIFETIME = 14400; // 60 * 60 * 4 
 
export const setCache = (key: string, value: any, cb: (data?: any) => void) => { 
 memcached.set(key, JSON.stringify(value), LIFETIME, error => { 
   if (error) { 
     logger('Memcached', JSON.stringify(error, null, 4)); 
   } 
 
   cb(); 
 }); 
} 
 
export const getCache = (key: string) => { 
 return new Promise<any>(resolve => { 
   memcached.get(key, (error, data) => { 
     resolve(data ? JSON.parse(data) : null); 
   }); 
 }); 
}; 
 
Лістинг файлу: /server/src/utils/converter.util.ts 
 
const DMap = { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 
11: 11, 12: 12, 13: 13, 14: 14, 15: 15, 16: 16, 17: 17, 18: 18, 19: 19, 20: 20, 
21: 21, 22: 22, 23: 23, 24: 24, 25: 25, 26: 26, 27: 27, 28: 28, 29: 29, 30: 30, 
31: 31, 32: 32, 33: 33, 34: 34, 35: 35, 36: 36, 37: 37, 38: 38, 39: 39, 40: 40, 
41: 41, 42: 42, 43: 43, 44: 44, 45: 45, 46: 46, 47: 47, 48: 48, 49: 49, 50: 50, 
51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 57: 57, 58: 58, 59: 59, 60: 60, 
61: 61, 62: 62, 63: 63, 64: 64, 65: 65, 66: 66, 67: 67, 68: 68, 69: 69, 70: 70, 
71: 71, 72: 72, 73: 73, 74: 74, 75: 75, 76: 76, 77: 77, 78: 78, 79: 79, 80: 80, 
81: 81, 82: 82, 83: 83, 84: 84, 85: 85, 86: 86, 87: 87, 88: 88, 89: 89, 90: 90, 
91: 91, 92: 92, 93: 93, 94: 94, 95: 95, 96: 96, 97: 97, 98: 98, 99: 99, 100: 100, 
101: 101, 102: 102, 103: 103, 104: 104, 105: 105, 106: 106, 107: 107, 108: 108, 
109: 109, 110: 110, 111: 111, 112: 112, 113: 113, 114: 114, 115: 115, 116: 116, 
117: 117, 118: 118, 119: 119, 120: 120, 121: 121, 122: 122, 123: 123, 124: 124, 
125: 125, 126: 126, 127: 127, 1027: 129, 8225: 135, 1046: 198, 8222: 132, 1047: 
199, 1168: 165, 1048: 200, 1113: 154, 1049: 201, 1045: 197, 1050: 202, 1028: 170, 
160: 160, 1040: 192, 1051: 203, 164: 164, 166: 166, 167: 167, 169: 169, 171: 171, 
172: 172, 173: 173, 174: 174, 1053: 205, 176: 176, 177: 177, 1114: 156, 181: 181, 
182: 182, 183: 183, 8221: 148, 187: 187, 1029: 189, 1056: 208, 1057: 209, 1058: 
210, 8364: 136, 1112: 188, 1115: 158, 1059: 211, 1060: 212, 1030: 178, 1061: 213, 
1062: 214, 1063: 215, 1116: 157, 1064: 216, 1065: 217, 1031: 175, 1066: 218, 1067: 
219, 1068: 220, 1069: 221, 1070: 222, 1032: 163, 8226: 149, 1071: 223, 1072: 224, 
8482: 153, 1073: 225, 8240: 137, 1118: 162, 1074: 226, 1110: 179, 8230: 133, 1075: 
227, 1033: 138, 1076: 228, 1077: 229, 8211: 150, 1078: 230, 1119: 159, 1079: 231, 
1042: 194, 1080: 232, 1034: 140, 1025: 168, 1081: 233, 1082: 234, 8212: 151, 1083: 
235, 1169: 180, 1084: 236, 1052: 204, 1085: 237, 1035: 142, 1086: 238, 1087: 239, 
1088: 240, 1089: 241, 1090: 242, 1036: 141, 1041: 193, 1091: 243, 1092: 244, 8224: 
134, 1093: 245, 8470: 185, 1094: 246, 1054: 206, 1095: 247, 1096: 248, 8249: 139, 
1097: 249, 1098: 250, 1044: 196, 1099: 251, 1111: 191, 1055: 207, 1100: 252, 1038: 
161, 8220: 147, 1101: 253, 8250: 155, 1102: 254, 8216: 145, 1103: 255, 1043: 195, 
1105: 184, 1039: 143, 1026: 128, 1106: 144, 8218: 130, 1107: 131, 8217: 146, 1108: 
186, 1109: 190 } 
 
export const toWin1251UrlEncoded = (value: string): string => { 
 const encodedResult = []; 
7 
 
 
 for (let i = 0; i < value.length; i++) { 
   encodedResult.push(/[A-z0-9]/.test(value[i]) ? value[i] : `%${ 
DMap[value.charCodeAt(i)].toString(16).toUpperCase() }`); 
 } 
 
 return encodedResult.join(''); 
} 
 
Лістинг файлу: /server/src/utils/index.ts 
 
export * from './request.util'; 
export * from './parser.util'; 
export * from './converter.util'; 
export * from './cache.util'; 
 
Лістинг файлу: /server/src/utils/logger.util.ts 
 
export const logger = (type: string, message: string) => { 
 console.log(`[${ new Date().toLocaleString('ru') }]: ${ type.toUpperCase() }${ 
message }`); 
 console.log('\n'); 
}; 
 
Лістинг файлу: /server/src/utils/request.util.ts 
 
import FormData from 'form-data'; 
 
import { API_URL, API_USER_AGENT, REQUEST_DATA, REQUEST_N } from '../constants'; 
import { axios } from '../config/axios.config'; 
 
export type RequestType = 'schedule' | 'teachers' | 'groups'; 
 
const toQuery = data => Object.keys(data).map(key => [key, data[key]].map(item => 
item).join('=')).join('&'); 
 
type Data = { 
 group: string; 
 sdate?: string; 
 edate?: string; 
} 
 
export const request = async (type: RequestType, data?: Data): Promise<any> => { 
 const queryString = toQuery({ ...REQUEST_DATA[type], ...data }); 
 
 const res = await axios(`${ API_URL }?n=${ REQUEST_N[type] }${ !data ? `&${ 
queryString }` : '' }`, { 
   method: data ? 'POST' : 'GET', 
   responseType: 'arraybuffer', 
   headers: { 
     'User-Agent': API_USER_AGENT, 
     'Content-Type': 'text/plain; charset=utf-8' 
   }, 
   ...(data && { data: queryString }) 
 }); 
 
8 
 
 return data ? res.data : JSON.parse(res.data); 
} 
 
Лістинг файлу: /server/src/scripts/groups.script.ts 
 
import 'dotenv/config'; 
 
import { database } from '../config/database.config'; 
import { GroupService } from '../modules/group/group.service'; 
import { User } from '../modules/user/user.entity'; 
import { Group } from '../modules/group/group.entity'; 
 
const addGroupsToDB = async () => { 
 const groupService = new GroupService(); 
 
 // Getting all existing group names at the current moment 
 const groupNames = await groupService.getAllGroupsNames(); 
 // Getting all existing groups 
 const groups = await groupService.getAll(); 
 const existingGroupNames = groups.map(group => group.realName); 
 
 // Getting missing groups by filtering their names. 
 const missingGroupNames = groupNames.filter(groupName => 
!existingGroupNames.includes(groupName)); 
 
 console.log('\nMISSING GROUPS: ', JSON.stringify(missingGroupNames, null, 4), 
'\n'); 
 
 // Creating group for each received name 
 // (using "for loop" here because we need to work with a promise inside a loop). 
 for (let name of missingGroupNames) { 
   try { 
     await groupService.create(name); 
     console.log('\n', name, '\nCREATED\n'); 
   } catch (error) { 
     console.log('\n', name, '\nERROR\n', error, '\n'); 
     process.exitCode = 1; 
   } 
 } 
 
 console.log('\nDONE\nGROUPS CREATED: ', missingGroupNames.length); 
 
 process.exit(); 
}; 
 
database.createConnection([User, Group]).then(() => { 
 addGroupsToDB().catch(console.error) 
}); 
 
Лістинг файлу: /server/src/models/base.controller.ts 
 
import { Response } from 'express'; 
import { BadRequestError, ForbiddenError, NotFoundError } from 'routing-
controllers'; 
 
import { logger } from '../utils/logger.util'; 
 
9 
 
export abstract class BaseController { 
 public ok<T>(res: Response, dto: T): Response<T> { 
   return res.status(200).json(dto); 
 } 
 
 public created<T>(res: Response, dto: T): Response<T> { 
   return res.status(201).json(dto); 
 } 
 
 public fail(res: Response, error: any): Response { 
   logger('', `${ res.req.method } ${ res.req.path } 
"params": ${ JSON.stringify(res.req.params, null, 4) } 
"query": ${ JSON.stringify(res.req.query, null, 4) } 
"body": ${ JSON.stringify(res.req.body, null, 4) } 
"error": ${ JSON.stringify(error, null, 4) } 
   `); 
 
   return res.status(500).json({ message: error.message }); 
 } 
 
 public error(res: Response, error: NotFoundError | BadRequestError | 
ForbiddenError) { 
   if (!error.httpCode) { 
     return this.fail(res, error); 
   } 
 
   return res.status(error.httpCode).json(error); 
 } 
} 
 
Лістинг файлу: /server/src/modules/user/user.dto.ts 
 
import { Group } from '../group/group.entity'; 
import { EUserType } from './user.typedef'; 
 
export interface ICreateUserDTO { 
 chatId: number; 
 type: EUserType; 
 group?: string; 
 firstName?: string; 
 title?: string; 
 username?: string; 
} 
 
export interface IReturnCreateUserDTO { 
 updated?: boolean; 
 group: Group 
} 
 
export interface IUpdateUserDTO { 
 group?: string; 
 username?: string; 
 title?: string; 
 firstName?: string; 
} 
 
Лістинг файлу: /server/src/modules/user/user.typedef.ts 
10 
 
 
export enum EUserRole { 
 BANNED, 
 DEFAULT, 
 MODERATOR, 
 ADMIN 
} 
 
export enum EUserType { 
 PRIVATE = 'private', 
 GROUP = 'group', 
 SUPERGROUP = 'supergroup' 
} 
 
Лістинг файлу: /server/src/modules/user/user.entity.ts 
 
import { Entity, PrimaryGeneratedColumn, Column, Index, BaseEntity, 
CreateDateColumn, UpdateDateColumn, DeleteDateColumn, ManyToOne } from 'typeorm'; 
import { IsString, IsNotEmpty, IsNumber } from 'class-validator'; 
 
import { Group } from '../group/group.entity'; 
import { EUserRole, EUserType } from './user.typedef'; 
 
@Entity() 
export class User extends BaseEntity { 
 @PrimaryGeneratedColumn() 
 id: number; 
 
 @IsString() 
 @IsNotEmpty() 
 @Index({ unique: true }) 
 @Column() 
 chatId: string; 
 
 @IsString() 
 @Index() 
 @Column({ nullable: true }) 
 username?: string; 
 
 @IsString() 
 @Column({ nullable: true }) 
 firstName?: string; 
 
 @IsString() 
 @Column({ nullable: true }) 
 title?: string; 
 
 @ManyToOne(() => Group, group => group.users) 
 group?: Group; 
 
 @Index() 
 @Column('enum', { default: EUserRole.DEFAULT, enum: EUserRole }) 
 role: EUserRole; 
 
 @IsNotEmpty() 
 @Index() 
 @Column('enum', { enum: EUserType }) 
 type: EUserType; 
11 
 
 
 @CreateDateColumn() 
 createdAt: Date; 
 
 @UpdateDateColumn() 
 updatedAt: Date; 
 
 @DeleteDateColumn() 
 deletedAt: Date; 
} 
 
Лістинг файлу: /server/src/modules/user/user.service.ts 
 
import { getRepository } from 'typeorm'; 
import { validate } from 'class-validator'; 
import { NotFoundError, BadRequestError } from 'routing-controllers'; 
 
import { USER_INVALID, USER_NOT_FOUND, GROUP_NOT_FOUND, GROUP_INVALID } from 
'../../constants'; 
import { GroupService } from '../group/group.service'; 
import { User } from './user.entity'; 
import { ICreateUserDTO, IUpdateUserDTO } from './user.dto'; 
import { FindOneOptions } from 'typeorm/find-options/FindOneOptions'; 
import { Group } from '../group/group.entity'; 
 
export class UserService { 
 private userRepository = getRepository(User); 
 private groupService = new GroupService(); 
 
 public async get(chatId: string): Promise<User> { 
   const user = await this.getByChatId(chatId); 
   if (!user) throw new NotFoundError(USER_NOT_FOUND); 
 
   return user; 
 } 
 
 public async getAll(): Promise<any> { 
   const [users, total] = await this.userRepository.findAndCount({ withDeleted: 
true, relations: ['group'] }); 
 
   return { total, users }; 
 } 
 
 public async create(dto: ICreateUserDTO): Promise<Group> { 
 
   const [group, user] = await 
Promise.all([this.groupService.getOneByName(dto.group), 
this.getByChatId(dto.chatId.toString(), { withDeleted: true })]); 
   // removing if it's soft deleted 
   if (user?.deletedAt) await user.remove(); 
   if (user && !user.deletedAt) throw new BadRequestError(USER_INVALID); 
   if (dto.group && !group) throw new NotFoundError(GROUP_NOT_FOUND); 
 
   const instance = new User(); 
   instance.chatId = dto.chatId.toString(); 
   instance.group = group || null; 
   instance.title = dto.title; 
   instance.firstName = dto.firstName; 
12 
 
   instance.username = dto.username; 
   instance.type = dto.type; 
 
   const errors = await validate(instance); 
   if (errors.length) throw new BadRequestError(errors[0].property); 
 
   await instance.save(); 
 
   return group; 
 } 
 
 public async update(chatId: string, dto: IUpdateUserDTO): Promise<Group> { 
   if (!dto.group) throw new BadRequestError(GROUP_INVALID); 
 
   const [group, user] = await 
Promise.all([this.groupService.getOneByName(dto.group), 
this.getByChatId(chatId)]); 
   if (!user) throw new NotFoundError(USER_NOT_FOUND); 
   if (!group) throw new NotFoundError(GROUP_NOT_FOUND); 
 
   user.group = group; 
   user.firstName = dto.firstName || user.firstName; 
   user.username = dto.username || user.username; 
   user.title = dto.title || user.title; 
 
   const errors = await validate(user); 
   if (errors.length) throw new BadRequestError(errors[0].property); 
 
   await user.save(); 
 
   return group; 
 } 
 
 public async remove(chatId: string): Promise<User> { 
   // using userRepository instead of getByChatId because this call 
   // no need to have included models inside user 
   const user = await this.userRepository.findOne({ chatId }); 
   if (!user) throw new NotFoundError(USER_NOT_FOUND); 
 
   await user.softRemove(); 
 
   return user; 
 } 
 
 // PRIVATE 
 
 private async getByChatId(chatId: string, options?: FindOneOptions<User>): 
Promise<User> { 
   return this.userRepository.findOne({ chatId }, { ...options, relations: 
['group'] }); 
 } 
} 
 
Лістинг файлу: /server/src/modules/user/user.controller.ts 
 
import { Body, Delete, Get, JsonController, Param, Post, Put, Res } from 'routing-
controllers'; 
import { Response } from 'express'; 
13 
 
 
import { BaseController } from '../base.controller'; 
import { ICreateUserDTO, IUpdateUserDTO } from './user.dto'; 
import { UserService } from './user.service'; 
 
@JsonController('/users') 
export class UserController extends BaseController { 
 private userService = new UserService(); 
 
 @Get() 
 public async getAll(@Res() res) { 
   try { 
     const user = await this.userService.getAll(); 
 
     return super.ok(res, user); 
   } catch (error) { 
     return super.error(res, error); 
   } 
 } 
 
 @Get('/:chatId') 
 public async get(@Param('chatId') chatId: string, @Res() res) { 
   try { 
     const user = await this.userService.get(chatId); 
 
     return super.ok(res, user); 
   } catch (error) { 
     return super.error(res, error); 
   } 
 } 
 
 @Post() 
 public async create(@Body() body: ICreateUserDTO, @Res() res: Response) { 
   try { 
     const user = await this.userService.create(body); 
 
     return super.created(res, user); 
   } catch (error) { 
     console.log(error); 
     return super.error(res, error); 
   } 
 } 
 
 @Put('/:chatId') 
 public async update(@Param('chatId') chatId: string, @Body() body: 
IUpdateUserDTO, @Res() res: Response) { 
   try { 
     const user = await this.userService.update(chatId, body); 
 
     return super.ok(res, user); 
   } catch (error) { 
     return super.error(res, error); 
   } 
 } 
 
 @Delete('/:chatId') 
 public async remove(@Param('chatId') chatId: string, @Res() res: Response) { 
   try { 
     const user = await this.userService.remove(chatId); 
14 
 
 
     return super.ok(res, user); 
   } catch (error) { 
     return super.error(res, error); 
   } 
 } 
} 
 
Лістинг файлу: /server/src/modules/group/group.entity.ts 
 
import { Entity, PrimaryGeneratedColumn, Column, Index, BaseEntity, 
CreateDateColumn, OneToMany } from 'typeorm'; 
import { IsString, IsNotEmpty } from 'class-validator'; 
 
import { User } from '../user/user.entity'; 
 
@Entity() 
export class Group extends BaseEntity { 
 @PrimaryGeneratedColumn() 
 id: number; 
 
 @IsNotEmpty() 
 @IsString() 
 @Index({ unique: true }) 
 @Column() 
 name: string; 
 
 @IsNotEmpty() 
 @IsString() 
 @Index({ unique: true }) 
 @Column() 
 realName: string; 
 
 @OneToMany(() => User, user => user.group) 
 users: User[]; 
 
 @CreateDateColumn() 
 createdAt: Date; 
} 
 
Лістинг файлу: /server/src/modules/group/group.service.ts 
 
import { getRepository } from 'typeorm'; 
 
import { REQUEST_TYPE } from '../../constants'; 
import { request } from '../../utils'; 
import { Group } from './group.entity'; 
 
export class GroupService { 
 private groupRepository = getRepository(Group); 
 
 public async getAllGroupsNames(): Promise<string[]> { 
   const data = await request(REQUEST_TYPE.groups, null); 
 
   return data.suggestions; 
 } 
 
15 
 
 public getAll(): Promise<Group[]> { 
   return this.groupRepository.find(); 
 } 
 
 public getOneByName(name: string): Promise<Group> { 
   return this.groupRepository.findOne({ name }); 
 } 
 
 public async create(name: string): Promise<Group> { 
   const group = new Group(); 
   group.name = name.toLowerCase(); 
   group.realName = name; 
 
   await group.save(); 
 
   return group; 
 } 
 
 public async removeAll() { 
   const groups = await this.getAll(); 
   await this.groupRepository.remove(groups); 
 } 
} 
 
Лістинг файлу: /server/src/modules/group/group.controller.ts 
 
import { JsonController, Get, QueryParams, Res, Req } from 'routing-controllers'; 
import { Request, Response } from 'express'; 
 
import { BaseController } from '../base.controller'; 
import { GroupService } from './group.service'; 
 
@JsonController('/groups') 
export class StatusController extends BaseController { 
 private readonly groupService = new GroupService(); 
 
 @Get() 
 public async get(@Req() _: Request, @Res() res: Response) { 
   try { 
     const groups = await this.groupService.getAll(); 
 
     return super.ok(res, groups); 
   } catch (error) { 
     return super.error(res, error); 
   } 
 } 
 
 @Get('/names') 
 public async getNames(@QueryParams({ required: true }) query: any, @Res() res: 
Response) { 
   try { 
     const groups = await this.groupService.getAllGroupsNames(); 
 
     return super.ok(res, groups); 
   } catch (error) { 
     return super.error(res, error); 
   } 
 } 
16 
 
} 
 
Лістинг файлу: /server/src/modules/schedule/schedule.dto.ts 
 
export interface IGetScheduleDTO { 
 chatId: string; 
 startDate: string; 
 endDate: string; 
 group: string; 
} 
 
Лістинг файлу: /server/src/modules/schedule/schedule.service.ts 
 
import { NotFoundError } from 'routing-controllers'; 
 
import { GROUP_NOT_FOUND, REQUEST_TYPE, USER_NOT_FOUND } from '../../constants'; 
import { request, parseSchedule, toWin1251UrlEncoded, getCache, setCache } from 
'../../utils'; 
import { UserService } from '../user/user.service'; 
import { GroupService } from '../group/group.service'; 
import { IGetScheduleDTO } from './schedule.dto'; 
 
export class ScheduleService { 
 private readonly userService = new UserService(); 
 private readonly groupService = new GroupService(); 
 
 public async get(query: IGetScheduleDTO) { 
   if (query.group) { 
     const group = decodeURIComponent(query.group); 
     const schedule = await this.getSchedule(group, query.startDate, 
query.endDate); 
 
     return schedule; 
   } 
 
   const user = await this.userService.get(query.chatId); 
   if (!user?.group) throw new NotFoundError(USER_NOT_FOUND); 
 
   const schedule = await this.getSchedule(user.group.name, query.startDate, 
query.endDate, true); 
 
   return schedule; 
 } 
 
 // PRIVATE 
 
 private async getSchedule(groupName: string, sdate: string, edate: string, 
skipGroupCheck?: boolean) { 
   const cacheKey = `${ groupName }-${ sdate }-${ edate }`; 
 
   const cache = await getCache(cacheKey); 
   if (cache) return cache; 
 
   if (!skipGroupCheck) { 
     const group = await this.groupService.getOneByName(groupName.toLowerCase()); 
     if (!group) throw new NotFoundError(GROUP_NOT_FOUND); 
   } 
17 
 
 
   const data = await request(REQUEST_TYPE.schedule, { group: 
toWin1251UrlEncoded(groupName), sdate, edate }); 
   const parsedData = parseSchedule(data); 
 
   // using callback here for event loop blocking avoidance 
   setCache(cacheKey, parsedData, () => {}); 
 
   return Object.keys(parsedData).length ? parsedData : null; 
 } 
} 
 
Лістинг файлу: /server/src/modules/schedule/schedule.controller.ts 
 
import { JsonController, Get, QueryParams, Res } from 'routing-controllers'; 
import { Response } from 'express'; 
 
import { BaseController } from '../base.controller'; 
import { ScheduleService } from './schedule.service'; 
import { IGetScheduleDTO } from './schedule.dto'; 
 
@JsonController('/schedule') 
export class StatusController extends BaseController { 
 private readonly scheduleService = new ScheduleService(); 
 
 @Get() 
 public async get(@QueryParams({ required: true }) query: IGetScheduleDTO, @Res() 
res: Response) { 
   try { 
     const schedule = await this.scheduleService.get(query); 
 
     return super.ok(res, schedule); 
   } catch (error) { 
     return super.error(res, error); 
   } 
 } 
} 
 
Лістинг файлів клієнтської частини 
 
Лістинг файлу: /client/Dockerfile 
 
FROM node:14 as base 
 
ENV CLIENT_ROOT /client 
 
RUN mkdir $CLIENT_ROOT 
WORKDIR $CLIENT_ROOT 
 
COPY ./package.json ./ 
 
RUN yarn 
 
COPY . $CLIENT_ROOT 
 
FROM base as production 
18 
 
 
RUN yarn build 
 
Лістинг файлу: /client/package.json 
 
{ 
 "name": "schedule-bot-client", 
 "version": "0.0.1", 
 "description": "", 
 "author": { 
   "name": "Max Zinchenko", 
   "url": "https://github.com/maxzinchenko" 
 }, 
 "keywords": ["Cherkasy State Technological University"], 
 "license": "MIT", 
 "main": "dist/index.js", 
 "engines": { 
   "node": ">=14 <15" 
 }, 
 "scripts": { 
   "start:dev": "cross-env NODE_ENV=development ./node_modules/.bin/nodemon 
src/index.ts --watch", 
   "start:prod": "cross-env NODE_ENV=production node dist/index.js", 
   "build": "rm -rf dist && cross-env NODE_ENV=production tsc" 
 }, 
 "dependencies": { 
   "axios": "0.21.1", 
   "node-telegram-bot-api": "0.51.0" 
 }, 
 "devDependencies": { 
   "@types/node": "14.14.34", 
   "@types/node-telegram-bot-api": "0.51.1", 
   "cross-env": "7.0.3", 
   "dotenv": "8.2.0", 
   "nodemon": "2.0.7", 
   "ts-node": "9.1.1", 
   "typescript": "4.2.3" 
 } 
} 
 
Лістинг файлу: /client/src/config/index.ts 
 
export * from './request.config'; 
 
Лістинг файлу: /client/src/config/request.config.ts 
 
import axios from 'axios'; 
 
import { SERVER_URL } from '../constants'; 
 
axios.defaults.baseURL = SERVER_URL; 
 
export const request = { 
 getSchedule: (params: any) => axios.get(`${ SERVER_URL }/schedule?${ new 
URLSearchParams(params).toString() }`), 
 getUser: (id: number) => axios.get(`${ SERVER_URL }/users/${ id }`), 
19 
 
 getUsers: () => axios.get(`${ SERVER_URL }/users`), 
 createUser: (data: any) => axios.post(`${ SERVER_URL }/users`, data), 
 updateUser: (id: number, data: any) => axios.put(`${ SERVER_URL }/users/${ id }`, 
data), 
 removeUser: (id: number) => axios.delete(`${ SERVER_URL }/users/${ id }`) 
}; 
 
Лістинг файлу: /client/src/constants/config.constants.ts 
 
export const CLIENT_PORT = parseInt(process.env.CLIENT_PORT); 
 
export const BOT_TOKEN = process.env.BOT_TOKEN; 
export const BOT_ID = process.env.BOT_ID; 
export const BOT_USERNAME = process.env.BOT_USERNAME; 
 
export const OWNER_ID = parseInt(process.env.OWNER_ID!); 
export const OWNER_USERNAME = process.env.OWNER_USERNAME; 
 
export const ADMIN_ID = parseInt(process.env.ADMIN_ID!); 
 
export const SERVER_URL = process.env.SERVER_URL; 
 
export const ADMIN_IDS = [OWNER_ID, ADMIN_ID]; 
 
Лістинг файлу: /client/src/constants/error.constants.ts 
 
import { OWNER_USERNAME } from './config.constants'; 
 
export enum ApiErrorStatus { 
 serverNotReady = 'SERVER_NOT_READY', 
 groupNotFound = 'GROUP_NOT_FOUND', 
 groupInvalid = 'GROUP_INVALID', 
 groupTypeInvalid = 'GROUP_TYPE_INVALID', 
 groupAlreadyAdded = 'GROUP_ALREADY_ADDED', 
 userNotFound = 'USER_NOT_FOUND', 
 forbidden = 'FORBIDDEN', 
 serverError = 'SERVER_ERROR' 
} 
 
export type ApiErrorMessage = { 
 main: string; 
 sub: null | string; 
}; 
 
export type ApiErrors = { [key in ApiErrorStatus]: ApiErrorMessage }; 
 
export const apiErrors: ApiErrors = { 
 [ApiErrorStatus.serverNotReady]: { 
   main: 'На даний момент Бот недоступний. Будь ласка, спробуйте пізніше.', 
   sub: 'Ми вже процюємо над цим. Вибачаємось за незручності.' 
 }, 
 [ApiErrorStatus.groupInvalid]: { 
   main: 'Не валідна назва групи.', 
   sub: null 
 }, 
 [ApiErrorStatus.groupTypeInvalid]: { 
   main: 'У вас SUPERGROUP група.', 
20 
 
   sub: 'Потрібні права адміністратора щоб бот працював у такій групі або змініть 
тип групи.' 
 }, 
 [ApiErrorStatus.groupAlreadyAdded]: { 
   main: 'Ви вже додали цю групу.', 
   sub: 'Щоб оновити групу введіть: /group.' 
 }, 
 [ApiErrorStatus.groupNotFound]: { 
   main: 'Групу <b>_</b> не знайдено.', 
   sub: 'Щоб cпробувати ще введіть: /group.' 
 }, 
 [ApiErrorStatus.userNotFound]: { 
   main: 'Ви ще не додали групу.', 
   sub: 'Щоб додати групу введіть: /group.' 
 }, 
 [ApiErrorStatus.userNotFound]: { 
   main: 'Ви ще не додали групу.', 
   sub: 'Щоб додати групу введіть: /group.' 
 }, 
 [ApiErrorStatus.forbidden]: { 
   main: 'Відмовлено у доступі.', 
   sub: `Щоб отримати доступ зверніться до @${ OWNER_USERNAME }.\n\nВаш унікальний 
код: <b>_</b>.` 
 }, 
 [ApiErrorStatus.serverError]: { 
   main: 'Сталася помилка. Будь ласка, спробуйте ще.', 
   sub: 'Ми вже процюємо над цим. Вибачаємось за незручності.' 
 } 
}; 
 
Лістинг файлу: /client/src/constants/index.ts 
 
export * from './config.constants'; 
export * from './bot.constants'; 
export * from './error.constants'; 
 
Лістинг файлу: /client/services/api.service.ts 
 
import { request } from '../config'; 
import { AxiosError } from 'axios'; 
 
import { apiErrors, ApiErrorMessage, ApiErrorStatus } from '../constants'; 
 
const ECONNREFUSED = 'ECONNREFUSED'; 
 
export class ApiService { 
 public getSchedule() { 
 
 } 
 
 public async getUser(id: number, callback): Promise<void> { 
   try { 
     // const res = await request.getUser(id); 
     // callback(null, res.data); 
   } catch (error) { 
     console.log(error); 
     callback(this.getErrorMessage(error)); 
21 
 
   } 
 } 
 
 public async createUser(data: any, callback): Promise<void> { 
   try { 
     const res = await request.createUser(data); 
     callback(null, res.data); 
   } catch (error) { 
     console.log(error); 
     callback(this.getErrorMessage(error)); 
   } 
 } 
 
 public async updateUser(id: number, data: any, callback): Promise<void> { 
   try { 
     const res = await request.updateUser(id, data); 
     console.log(res); 
     callback(null, res.data); 
   } catch (error) { 
     console.log(error); 
     callback(this.getErrorMessage(error)); 
   } 
 } 
 
 public async removeUser(id: number, callback): Promise<void> { 
   try { 
     const res = await request.removeUser(id); 
     console.log(res); 
     callback(null, res.data); 
   } catch (error) { 
     callback(this.getErrorMessage(error)); 
   } 
 } 
 
 private getErrorMessage(error: AxiosError): ApiErrorMessage { 
   if (error.code === ECONNREFUSED) return 
apiErrors[ApiErrorStatus.serverNotReady]; 
 
   return apiErrors[error.response?.data?.message] || 
apiErrors[ApiErrorStatus.serverError]; 
 } 
} 
 
Лістинг файлу: /client/services/bot.service.ts 
 
import TelegramBot, { ConstructorOptions, Message } from 'node-telegram-bot-api'; 
 
import { apiErrors, ApiErrorMessage, OWNER_ID, BOT_USERNAME, BOT_ID, ADMIN_IDS } 
from '../constants'; 
import { request } from '../config'; 
import { ApiService } from './api.service'; 
import { ScheduleService } from './scedule.service'; 
 
// Public 
const groupRegex = new RegExp(`^\/(group|group@${ BOT_USERNAME })$`, 'ig'); 
const clearRegex = new RegExp(`^\/(clear|clear@${ BOT_USERNAME })$`, 'ig'); 
const permissionsRegex = new RegExp(`^\/(permissions|permissions@${ BOT_USERNAME 
})$`, 'ig'); 
22 
 
const todayRegex = new RegExp(`^\/(today|today@${ BOT_USERNAME })$`, 'ig'); 
const tomorrowRegex = new RegExp(`^\/(tomorrow|tomorrow@${ BOT_USERNAME })$`, 
'ig'); 
const weekRegex = new RegExp(`^\/(week|week@${ BOT_USERNAME })`, 'ig'); 
const answerRegex = new RegExp(/^(?!\/.*$).*/g); 
// Private 
const usersRegex = new RegExp(`^\/(users|users@${ BOT_USERNAME })$`, 'ig'); 
const pmRegex = new RegExp(`^\/(pm|pm@${ BOT_USERNAME }) \w*`, 'ig'); 
 
export class BotService extends TelegramBot { 
 private apiService = new ApiService(); 
 private scheduleService = new ScheduleService(); 
 
 constructor(token: string, options?: ConstructorOptions) { 
   super(token, options); 
 } 
 
 public addPermissionsListener() { 
   this.onText(permissionsRegex, async parent => { 
     await this.checkMessage(parent, async () => { 
       await this.sendMessage(parent.chat.id, 'З якою метою Ви хочете отримати 
доступ до недоступних команд?'); 
 
       this.onText(answerRegex, async data => { 
         if (data.from.id === parent.from.id) { 
           await this.sendMessage(parent.chat.id, 'З Вами скоро 
зв\'яжуться.\n\n<b>Дякуємо, що ви з нами!</b>', { parse_mode: 'HTML' }); 
           await this.sendHeart(data.chat.id); 
           await this.sendMessage(OWNER_ID, JSON.stringify(data, null, 4)); 
 
           this.removeTextListener(answerRegex); 
         } 
       }); 
     }); 
   }); 
 } 
 
 public addGroupListener() { 
   this.onText(groupRegex, async parent => { 
     await this.checkMessage(parent, async () => { 
       let oldGroup; 
 
       try { 
         const { data } = await request.getUser(parent.chat.id); 
         oldGroup = data.group; 
       } catch {} 
 
       await this.sendMessage(parent.chat.id, 'Введіть назву групи\n<b>(регістр не 
має значення)</b>', { parse_mode: 'HTML' }); 
 
       this.onText(answerRegex, async ({ chat, from, text }) => { 
         if (from.id === parent.from.id) { 
           const group = text.trim().toLowerCase(); 
 
           if (oldGroup) await this.updateGroup(chat.id, from.username, 
from.first_name, group, oldGroup.realName, chat.title || ''); 
           else await this.addGroup(chat.id, from.username, from.first_name, 
group, chat.type, chat.title || ''); 
 
23 
 
           this.removeTextListener(answerRegex); 
         } 
       }); 
     }); 
   }); 
 } 
 
 public addClearListener() { 
   this.onText(clearRegex, async msg => { 
     await this.checkMessage(msg, async () => { 
       const { id } = msg.chat; 
 
       try { 
         await request.removeUser(id); 
         await this.sendMessage(id, 'Групу успішно видалено.\n\n<b>Дякуємо, що 
були з нами!</b>', { parse_mode: 'HTML' }); 
         this.sendHeart(id); 
       } catch (error) { 
         await this.sendException(id, apiErrors[error.response?.data?.message] || 
apiErrors.SERVER_ERROR); 
       } 
     }); 
   }); 
 } 
 
 public addMessageListener() { 
   // return this.onText(messageRegex, async ({ chat, from, text }) => { 
   //   await this.apiService.getUser(chat.id, async (error: ApiErrorMessage, res) 
=> { 
   //     // if (error) return this.sendException(chat.id, error); 
   // 
   //     // if (from?.id === OWNER_ID) return this.sendException(chat.id, 
apiErrors[ApiErrorStatus.FORBIDDEN], chat.id); 
   // 
   //     await this.sendMessage(chat.id, JSON.stringify({ chat, from, text }, 
null, 4)); 
   //   }); 
   // }); 
 } 
 
 public addTodayListener() { 
   this.onText(todayRegex, async msg => { 
     await this.checkMessage(msg, async () => { 
       await this.getSchedule(msg.chat.id, 'today'); 
     }); 
   }); 
 } 
 
 public addTomorrowListener() { 
   this.onText(tomorrowRegex, async msg => { 
     await this.checkMessage(msg, async () => { 
       await this.getSchedule(msg.chat.id, 'tomorrow'); 
     }); 
   }); 
 } 
 
 public addWeekListener() { 
   this.onText(weekRegex, async msg => { 
     await this.checkMessage(msg, async () => { 
24 
 
       await this.getSchedule(msg.chat.id, 'week'); 
     }); 
   }); 
 } 
 
 public addUsersPrivateListener() { 
   this.onText(usersRegex, msg => { 
     this.checkPrivateMessage(msg, async () => { 
       try { 
         const { data } = await request.getUsers(); 
         await this.sendMessage(msg.chat.id, `<b>Користувачiв:</b> ${ data.total 
}`, { parse_mode: 'HTML' }); 
         await this.sendMessage(msg.chat.id, JSON.stringify(data.users, null, 4)); 
       } catch (error) { 
         await this.sendException(msg.chat.id, 
apiErrors[error.response?.data?.message] || apiErrors.SERVER_ERROR); 
       } 
     }); 
   }); 
 } 
 
 public addPMPrivateListener() { 
   this.onText(pmRegex, msg => { 
     this.checkPrivateMessage(msg, async () => { 
       const receiverChatId = +msg.text.replace(/\/\w* /, ''); 
 
       await this.sendMessage(msg.chat.id, `Введiть ваше повiдомлення для <b>${ 
receiverChatId }</b>`, { parse_mode: 'HTML' }); 
 
       this.onText(answerRegex, async ({ text }) => { 
         Promise.all([ 
           this.sendMessage(receiverChatId, text), 
           this.sendMessage(msg.chat.id, 'Повiдомлення вiдправлено') 
         ]); 
 
         this.removeTextListener(answerRegex); 
       }); 
     }); 
   }); 
 } 
 
 // PRIVATE 
 
 private sendException(chatId: number, error: ApiErrorMessage | string, 
replaceValue?: number | string) { 
   const msg = `<b>${ typeof error === 'string' ? error : `${ error.main 
}</b>\n\n${ error.sub || '' }` }`; 
   const msgReplaced = replaceValue ? msg.replace(/_/, replaceValue.toString()) : 
msg; 
 
   this.sendMessage(chatId, msgReplaced, { parse_mode: 'HTML' }); 
 } 
 
 private sendHeart(chatId: number) { 
   setTimeout(async () => { 
     await this.sendMessage(chatId, '\u{2764}'); 
   }, 100); 
 } 
 
25 
 
 private async addGroup(chatId: number, username: string, firstName: string, 
group: string, type: string, title: string) { 
   try { 
     const dto = { chatId, username, firstName, group, type, title }; 
 
     const res = await request.createUser(dto); 
     Promise.all([ 
       this.sendMessage(chatId, `Групу <b>${ res.data.realName }</b> успішно 
додано.`, { parse_mode: 'HTML' }), 
       this.sendMessage(OWNER_ID, `<b>Новий користувач:</b>\n\n${ 
JSON.stringify(dto, null, 4) }`, { parse_mode: 'HTML' }), 
     ]); 
     await this.sendKeyboard(chatId); 
   } catch (error: any) { 
     await this.sendException(chatId, apiErrors[error.response?.data?.message] || 
apiErrors.SERVER_ERROR, group); 
   } 
 } 
 
 private async updateGroup(chatId: number, username: string, firstName: string, 
group: string, prevGroup: string, title: string) { 
   try { 
     if (group.toLowerCase() === prevGroup.toLowerCase()) { 
       await this.sendException(chatId, apiErrors.GROUP_ALREADY_ADDED); 
       return; 
     } 
 
     const res = await request.updateUser(chatId, { username, firstName, group, 
title }); 
     await this.sendMessage(chatId, `Групу <b>${ prevGroup }</b> успішно оновлено 
на <b>${ res.data.realName }</b>.`, { parse_mode: 'HTML' }); 
     await this.sendKeyboard(chatId); 
   } catch (error: any) { 
     await this.sendException(chatId, apiErrors[error.response?.data?.message] || 
apiErrors.SERVER_ERROR, group); 
   } 
 } 
 
 private async getSchedule(chatId: number, period: 'today' | 'tomorrow' | 'week') 
{ 
   try { 
     const dates = this.scheduleService.period(period); 
     const res = await request.getSchedule({ chatId, ...dates }); 
 
     if (!Object.keys(res.data.schedule).length) { 
       await this.sendMessage(chatId, '<b>Запланованих пар не знайдено!</b>', { 
parse_mode: 'HTML' }); 
     } else { 
       const days = await this.scheduleService.convertSchedule(res.data.schedule); 
 
       await days.forEach(day => { 
         this.sendMessage(chatId, day.split(',').join(''), { parse_mode: 'HTML' 
}); 
       }); 
 
       await setTimeout(async () => { 
         await this.sendMessage(chatId, `<a href="${ res.data.check }">Перевірити 
розклад</a>`, { parse_mode: 'HTML' }); 
       }, 1000); 
26 
 
     } 
   } catch (error) { 
     await this.sendException(chatId, apiErrors[error.response?.data?.message] || 
apiErrors.SERVER_ERROR); 
   } 
 } 
 
 private async sendKeyboard(chatId: number) { 
   await this.sendMessage(chatId, 'Розклад:', { 
     reply_markup: { 
       keyboard: [ 
         [{ text: '/today' }, { text: '/tomorrow' }, { text: '/week' }] 
       ], 
       force_reply: false, 
       one_time_keyboard: true, 
       resize_keyboard: true, 
     } 
   }); 
 } 
 
 private async checkMessage({ from, chat }: Message, cb: () => void) { 
   this.removeAllListeners(); 
 
   if (from.is_bot) { 
     this.sendException(chat.id, apiErrors.FORBIDDEN, chat.id); 
     return; 
   } 
 
   const botData =  await this.getChatMember(chat.id, BOT_ID); 
 
   if (chat.type === 'supergroup' && botData.status !== 'administrator') { 
     this.sendException(chat.id, apiErrors.GROUP_TYPE_INVALID); 
     return; 
   } 
 
   cb(); 
 } 
 
 private checkPrivateMessage({ from, chat }: Message, cb: () => void) { 
   this.removeAllListeners(); 
 
   if (!ADMIN_IDS.includes(from.id)) { 
     this.sendException(chat.id, apiErrors.FORBIDDEN, chat.id); 
     return; 
   } 
 
   cb(); 
 } 
} 
 
Лістинг файлу: /client/services/index.ts 
 
export * from './bot.service'; 
 
Лістинг файлу: /client/services/scedule.service.ts 
 
export class ScheduleService { 
27 
 
 public convertSchedule(data: any) { 
   const days = Object.keys(data); 
 
   return days.map(key => this.convertDay(key, data[key])); 
 } 
 
 public period(period: string) { 
   const today = new Date(); 
 
   const setPeriod = (start: Date, end: Date) => ({ 
     startDate: start.toLocaleDateString('ru'), 
     endDate: end.toLocaleDateString('ru') 
   }); 
 
   switch (period) { 
     case 'today': 
       return setPeriod(today, today); 
 
     case 'tomorrow': 
       const tomorrow = new Date(today); 
       tomorrow.setDate(tomorrow.getDate() + 1); 
 
       return setPeriod(tomorrow, tomorrow); 
 
     default: 
       const week = new Date(today); 
       week.setDate(week.getDate() + 7); 
 
       return setPeriod(today, week); 
   } 
 } 
 
 // PRIVATE 
 
 private convertDay(day: string, subjects: any[]) { 
   return `\n\n\n<b>\u{1F5D3} ${ day }</b>\n${ subjects.map(this.convertSubject) 
}` 
 } 
 
 private convertSubject(subject: any, idx: number) { 
   return ` 
<i>Пара: <b>${ idx + 1 }</b></i> 
${ !subject ? '<b>Немає</b>' : `<b>Предмет:</b> ${ subject.subject } 
<b>Викладач:</b> ${ subject.teacher } 
<b>Аудиторiя:</b> ${ subject.audience } 
<b>Збірна група:</b> ${ subject.group || 'Нi' } 
<b>Час:</b> ${ subject.startAt }-${ subject.endAt }` } 
`; 
 } 
} 
 
Лістинг файлу: /client/services/client.ts 
 
import http from 'http'; 
 
import { BotService } from './services'; 
 
export class Client { 
28 
 
 private botService: BotService; 
 
 constructor(private readonly port: number, private readonly token: string) { 
   this.port = port; 
   this.botService = new BotService(token, { polling: true }); 
 } 
 
 public run() { 
   const client = http.createServer(); 
 
   client.listen(this.port); 
 
   console.log(`\nClient is up and running on port ${ this.port }\n`) 
 } 
 
 public resetListeners() { 
   this.botService.removeAllListeners(); 
 
   console.log(`\nListeners have been removed\n`); 
 } 
 
 public initListeners() { 
   // Public 
   this.botService.addGroupListener(); 
   this.botService.addClearListener(); 
   this.botService.addTodayListener(); 
   this.botService.addTomorrowListener(); 
   this.botService.addWeekListener(); 
   this.botService.addPermissionsListener(); 
   this.botService.addMessageListener(); 
   // Private 
   this.botService.addUsersPrivateListener(); 
   this.botService.addPMPrivateListener(); 
 
   console.log(`\nListeners have been added\n`); 
 } 
} 
 
Лістинг файлу: /client/services/index.ts 
 
import 'dotenv/config'; 
 
import { CLIENT_PORT, BOT_TOKEN } from './constants'; 
import { Client } from './client'; 
 
const client = new Client(CLIENT_PORT, BOT_TOKEN); 
 
client.run(); 
client.resetListeners(); 
client.initListeners(); 
 
Лістинг файлів DevOps частини 
 
Лістинг файлу: /docker/docker-compose.dev.yml 
 
version: '3.7' 
29 
 
 
services: 
 
 server: 
   build: 
     target: base 
   command: bash -c 'while !</dev/tcp/database/5555; do sleep 1; done; yarn 
start:dev;' 
 
 database: 
   volumes: 
     - ./init-database.dev.sh:/docker-entrypoint-initdb.d/init-database.sh 
 
 client: 
   build: 
     target: base 
   command: bash -c 'yarn start:dev;' 
 
Лістинг файлу: /docker/docker-compose.prod.yml 
 
version: '3.7' 
 
services: 
 
 server: 
   build: 
     target: production 
   command: bash -c 'while !</dev/tcp/database/5555; do sleep 1; done; yarn 
start:prod;' 
 
 database: 
   volumes: 
     - ./init-database.prod.sh:/docker-entrypoint-initdb.d/init-database.sh 
 
 client: 
   build: 
     target: production 
   command: bash -c 'yarn start:prod;' 
 
Лістинг файлу: /docker/docker-compose.yml 
 
version: '3.7' 
 
services: 
 
 server: 
   container_name: server 
   restart: unless-stopped 
   build: 
     context: ../server 
     dockerfile: Dockerfile 
   depends_on: 
     - database 
     - cache 
   ports: 
     - $SERVER_PORT:$SERVER_PORT 
   env_file: 
30 
 
     - ../.env 
   volumes: 
     - ../server/src:/server/src 
     - node_modules_server:/home/node/app/node_modules 
   networks: 
     - botnet 
 
 database: 
   container_name: database 
   image: postgres:latest 
   restart: unless-stopped 
   ports: 
     - $POSTGRES_PORT:5432 
   env_file: 
     - ../.env 
   volumes: 
     - ./volumes/database:/var/lib/postgresql/data:rw 
   networks: 
     - botnet 
   command: -p $POSTGRES_PORT 
 
 cache: 
   container_name: cache 
   image: memcached:latest 
   restart: unless-stopped 
   ports: 
     - $MEMCACHED_PORT:11211 
   volumes: 
     - ./volumes/cache:/memcached 
   networks: 
     - botnet 
   command: -p $MEMCACHED_PORT 
 
 client: 
   container_name: client 
   restart: unless-stopped 
   build: 
     context: ../client 
     dockerfile: Dockerfile 
   depends_on: 
     - server 
   ports: 
     - $CLIENT_PORT:$CLIENT_PORT 
   env_file: 
     - ../.env 
   volumes: 
     - ../client/src:/client/src 
     - node_modules_client:/home/node/app/node_modules 
   networks: 
     - botnet 
 
networks: 
 botnet: 
   driver: bridge 
 
volumes: 
 node_modules_server: 
 node_modules_client: 
 
31 
 
Лістинг файлу: /docker/init-database.dev.sh 
 
#!/bin/bash 
set -e 
 
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-
EOSQL 
   CREATE DATABASE schedule_development; 
EOSQL 
 
Лістинг файлу: /docker/init-database.prod.sh 
 
#!/bin/bash 
set -e 
 
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-
EOSQL 
   CREATE DATABASE schedule_production; 
EOSQL 
 
 
32