Спробую написати коротше та зрозуміліше.( Можливо загубив деталі )
Стан: : Краще почати з історії
Не так давно, я почав розробку власного телеграм боту. Бот має chain-message структуру, тобто коли користувач натискає кнопку/викликає команду/пише звичайне повідомлення спрацьовує задана функція з заданими параметрами з кнопки( вони знаходяться в полі callback_id
), а для того щоб передавати якісь параметри… ну…. я їх просто передавав за допомогою нижьної лінії. Вони мали ось такий вигляд:
superUsefulMethod_6317185824_saIgk3wO_10_require
…де метод superUseful
приймає user_id, file_id якогось малюнку, айді пакунка, і вже якась побічна дія.
Тобто ідея була кожного разу акумулювати параметри за допомогою купи функцій та в кінці їх виконувати в кінечній функції.
Так, саме так я й передавав всі данні в колбеки з кнопок, зберігав їх в базі данних прив’язаних до користувача. Для спрощення цієї процедури я ще створював допоміжні методи….
@required()
@action("cb")
async admSendMessage()
@required()
@action("msg")
async admSendMessage2(...args: string[])
@required()
@action("msg")
async admSendMessage3(...args: string[])
До речі так, у зв’язку з використанням TypeScript це ще я прикрасив декораторами. Але повертаючись до теми, структура проєкту мала дууууже жахливий вигляд, й читаємість коду була екстримально малою.
Після довгих роздумів я ризикнув ( ну точніше як ризикнув, нічого втрачати, аудиторія бота 30 людей ) й створив окрему табличку в БД:
(Я прибрав зайвий контент з панелькі)
model Action {
id String @unique
name String
params String? // An array actually
beenExecuted Boolean @default(false)
methods String?
}
І почав її додавати до всіх місць де треба було більше ніж одна дія. І буквально відразу же я зрозумів наскільки це є ефективніше й більш лаконічно.
Стан: : Структура
id - записується в стан користувача з префіксом, щоб можна було зрозуміти.
name/methods - це дозволені функції
params - масив з переданих данних, саме цей елемент відіграє значущу роль у вирішенні комплексних проблем.
Стан: : Як працює
Якщо користувач отримав цей стан і якщо користувач дійшов до функції яка є в name/methods, то вони виконуються з заданими параметрами. Це було чудове рішення, отже тепер я можу створити одну функцію й акумулювати параметри в одній функції.
Код отримав такий вигляд:
@required()
async admSendMessage(userId?: number, text?: String)
Тут всі параметри є опціональні. Насправді кожну наступну дію вони один за одним заповнюються. В самій функції відбувається перевірка на їх наявність.
Стан: : Плюси та Мінуси
Серед плюсів я би виділив:
Підвищує читаємість коду.
Спрощує всі до однієї функції, хоч і з трішки ускладненою логікую додаткових if/else.
Можна передавати більше корисної інформації параметрами.
Можна не думати про те, що колися ти досягнеш ліміту телеграму в 64 символа( а це можливо з якимись uuid )
Можливість маніпулювання станом.
А ще, це полегшує дебаг…
І нажаль є мінуси:
Функція стає трішки складніше.
Задіяна база данних. Можливо сповільнить асинхронність.
Стан: : Ще варіант
Це вже бонус:
На мою думку можна зберігати в /tmp файл з айді користувача та стана користувача. Через особливості TMPFS це робить все екстримально швидким. Мінус є один, і це те, якщо перезавантажити сервер, то стан зітреться.
Стан: : Кінцівка
Дякую що ви це прочитали. До речі, якщо вам цікаві схожі рішення, можете глянути тут: 🧼 Soap Opera & Art House 🎨