{"id":426520,"date":"2024-07-17T15:01:11","date_gmt":"2024-07-17T15:01:11","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=426520"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=426520","title":{"rendered":"<span>Ruby Telegram Mini App<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>1.1. \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043c\u0438\u043d\u0438 \u044d\u043f\u043f\u044b \u0438 \u0420\u0443\u0431\u0438<\/h3>\n<p>\u0421 \u043d\u0435\u0434\u0430\u0432\u043d\u0438\u0445 \u043f\u043e\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c Telegram \u0441\u0438\u043b\u044c\u043d\u043e \u0432\u044b\u0440\u043e\u0441\u043b\u0430. \u041f\u043e\u043c\u0438\u043c\u043e \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u0431\u043e\u0442\u043e\u0432, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u044f\u0440\u043a\u043e \u0432\u044b\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f Telegram Mini Apps. \u0418\u0437\u0443\u0447\u0438\u0432, \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0443 \u0430\u0432\u0442\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0438\u0434\u0435\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u0432\u044b\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0437\u0438\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u043c.<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443, \u043f\u043e \u043c\u043d\u0435\u043d\u0438\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u044f\u0437\u044b\u043a \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f Ruby \u0438 \u0435\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Ruby on Rails \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u0438\u043b\u0443\u0447\u0448\u0438\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432, \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u0422\u0430\u043a\u0436\u0435 \u0432\u0430\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043d\u0430 GitHub \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043c\u0430\u043b\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043a\u043e\u0434\u0430 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Mini App \u043d\u0430 Ruby. \u0412\u0441\u0435\u043c\u0438\u0440\u043d\u0430\u044f \u043f\u0430\u0443\u0442\u0438\u043d\u0430 \u0442\u043e\u0436\u0435 \u043d\u0435 \u043e\u0441\u043e\u0431\u043e \u0440\u0430\u0434\u043e\u0432\u0430\u043b\u0430 \u043d\u0430\u0441 \u0440\u0430\u0431\u043e\u0447\u0438\u043c\u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0451\u0439 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0432\u043d\u0435\u0441\u0442\u0438 \u0441\u0432\u043e\u0439 \u043f\u043e\u0441\u0438\u043b\u044c\u043d\u044b\u0439 \u0432\u043a\u043b\u0430\u0434 \u0432 \u0441\u0444\u0435\u0440\u0443, \u043a\u0430\u0441\u0430\u044e\u0449\u0443\u044e\u0441\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430 Ruby.<\/p>\n<h3>1.2. \u0424\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u0430\u044f \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e\u0441\u0442\u044c<\/h3>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u0432\u044f\u0442\u0438\u043b\u0438 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044e \u0441\u0444\u0435\u0440\u044b \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u044b\u0445 \u0430\u043a\u0442\u0438\u0432\u043e\u0432 \u0438 \u043a\u0440\u0438\u043f\u0442\u043e\u0432\u0430\u043b\u044e\u0442, \u043d\u0430\u043c\u0438 \u0431\u044b\u043b\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0433\u0440\u0443, \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u0443\u044e\u0449\u0443\u044e \u0440\u044b\u043d\u043e\u0447\u043d\u0443\u044e \u0432\u043e\u043b\u0430\u0442\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0434\u043d\u043e\u0439 \u0432\u044b\u0434\u0443\u043c\u0430\u043d\u043d\u043e\u0439 \u0430\u043a\u0446\u0438\u0438. \u041d\u0430\u0437\u043e\u0432\u0451\u043c \u0435\u0451 &#171;AVA&#187; (an volatile asset), \u0447\u0442\u043e \u043f\u043e-\u0440\u0443\u0441\u0441\u043a\u0438 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 &#171;\u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0439 \u0430\u043a\u0442\u0438\u0432&#187;.<\/p>\n<p>\u0417\u0430\u0447\u0435\u043c \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u044b\u043d\u043e\u0447\u043d\u0443\u044e \u0432\u043e\u043b\u0430\u0442\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u043f\u0440\u0438 \u0447\u0435\u043c \u0442\u0443\u0442 \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u0430\u044f \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e\u0441\u0442\u044c?<\/p>\n<p>\u0418\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440\u044b \u0414\u0436\u043e\u043d \u0411\u043e\u0433\u043b \u0438 \u0411\u0435\u043d\u0434\u0436\u0430\u043c\u0438\u043d \u0413\u0440\u044d\u043c \u0432 \u0441\u0432\u043e\u0438\u0445 \u0440\u0430\u0431\u043e\u0442\u0430\u0445 \u0432\u044b\u0434\u0432\u0438\u043d\u0443\u043b\u0438 \u0442\u0435\u0437\u0438\u0441\u044b \u043e \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430\u0445 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0434 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u0440\u0442\u0444\u0435\u043b\u0435\u043c. \u0412 \u043a\u043d\u0438\u0433\u0435 &#171;\u0420\u0430\u0437\u0443\u043c\u043d\u044b\u0439 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440&#187; \u0413\u0440\u044d\u043c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u0441\u043b\u043e\u0436\u043d\u043e\u0435 \u0438 \u0447\u0430\u0441\u0442\u043e \u043d\u0435\u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440\u043e\u0432 \u0438\u0437-\u0437\u0430 \u0432\u044b\u0441\u043e\u043a\u0438\u0445 \u0438\u0437\u0434\u0435\u0440\u0436\u0435\u043a \u0438 \u043d\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432. \u041e\u043d \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440\u0430\u043c \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u0434\u043e\u043b\u0433\u043e\u0441\u0440\u043e\u0447\u043d\u044b\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u043d\u044b\u0435 \u0444\u043e\u043d\u0434\u044b \u0438\u043b\u0438 \u0434\u0438\u0432\u0435\u0440\u0441\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u0444\u0435\u043b\u0438 \u0430\u043a\u0446\u0438\u0439 \u0438 \u043e\u0431\u043b\u0438\u0433\u0430\u0446\u0438\u0439, \u0447\u0442\u043e \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0440\u0438\u0441\u043a\u0438 \u0438 \u0438\u0437\u0434\u0435\u0440\u0436\u043a\u0438. \u0411\u043e\u0433\u043b \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e \u0438\u043d\u0434\u0435\u043a\u0441\u043d\u044b\u0445 \u0444\u043e\u043d\u0434\u043e\u0432 \u043a\u0430\u043a \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u044f\u0441\u044c \u043e\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0448\u0438\u0440\u043e\u043a\u043e\u043c\u0443 \u0440\u044b\u043d\u043a\u0443 \u0441 \u043d\u0438\u0437\u043a\u0438\u043c\u0438 \u043a\u043e\u043c\u0438\u0441\u0441\u0438\u044f\u043c\u0438 \u0438 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0438\u0437\u0434\u0435\u0440\u0436\u043a\u0430\u043c\u0438.<\/p>\n<p>\u0420\u0435\u0437\u044e\u043c\u0438\u0440\u0443\u044f: \u043f\u043e \u043c\u043d\u0435\u043d\u0438\u044e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u0430\u0432\u0442\u043e\u0440\u043e\u0432, \u043f\u043e\u043f\u044b\u0442\u043a\u0438 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u0441\u043f\u0435\u043a\u0443\u043b\u044f\u0446\u0438\u044f\u0445 \u043d\u0430 \u0440\u044b\u043d\u043a\u0435 \u0446\u0435\u043d\u043d\u044b\u0445 \u0431\u0443\u043c\u0430\u0433 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u044b\u0433\u043e\u0434\u043d\u044b \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0434\u043e\u043b\u0433\u043e\u0441\u0440\u043e\u0447\u043d\u044b\u043c \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 &#171;\u043a\u0443\u043f\u0438\u043b \u0438 \u0437\u0430\u0431\u044b\u043b&#187;. \u041d\u0430\u0448\u0430 \u0438\u0433\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e. \u0427\u0442\u043e\u0431\u044b \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c \u0432 \u043d\u0435\u0439 \u0431\u043e\u043b\u044c\u0448\u0435, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043a\u0443\u043f\u0438\u0442\u044c \u0430\u043a\u0442\u0438\u0432 \u0438 \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0435\u0433\u043e, \u043d\u043e, \u043a\u0430\u043a \u0446\u0435\u043d\u0438\u0442\u0435\u043b\u0438 \u0430\u0437\u0430\u0440\u0442\u0430, \u043c\u044b \u0434\u0430\u0451\u043c \u0438\u0433\u0440\u043e\u043a\u0443 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0441\u043f\u0435\u043a\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u043a\u0430\u043a \u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0436\u0438\u0437\u043d\u0438, \u043d\u0430\u0439\u0434\u0443\u0442\u0441\u044f \u0441\u0447\u0430\u0441\u0442\u043b\u0438\u0432\u0447\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043c\u043e\u0433\u0443\u0442 \u0441\u043b\u043e\u0432\u0438\u0442\u044c \u0443\u0434\u0430\u0447\u0443 \u0438 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u0432\u0430\u043b\u044e\u0442\u0443, \u043e\u0434\u043d\u0430\u043a\u043e \u0441\u043f\u0435\u043a\u0443\u043b\u044f\u043d\u0442\u044b \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0447\u0430\u0441\u0442\u043e \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u044e\u0442 \u043d\u0430 \u0434\u043b\u0438\u043d\u043d\u044b\u0445 \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u044f\u0445.<\/p>\n<h3>2.1. \u0424\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438\u0433\u0440\u044b<\/h3>\n<p>\u041f\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0432\u044b\u0448\u0435, \u0438\u0433\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0438\u043c\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c: <\/p>\n<ol>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Telegram; <\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0434\u0430\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e &#171;\u043b\u0438\u0441\u0442\u0430\u0442\u044c&#187;, \u0442.\u0435. \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 &#171;\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0434\u0430\u0442\u0430&#187; \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0440\u043e\u043c\u043e\u0442\u0430\u0442\u044c\u0441\u044f \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 n \u0435\u0434\u0438\u043d\u0438\u0446 \u0432\u043f\u0435\u0440\u0435\u0434, \u0447\u0442\u043e\u0431\u044b \u0438\u0433\u0440\u043e\u043a \u043d\u0435 \u0436\u0434\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0446\u0435\u043d\u044b; <\/p>\n<\/li>\n<li>\n<p>\u0426\u0435\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u043a\u043e\u043b\u0435\u0431\u0430\u0442\u044c\u0441\u044f \u0434\u0435\u043d\u044c \u043e\u0442\u043e \u0434\u043d\u044f; <\/p>\n<\/li>\n<li>\n<p>\u0413\u0440\u0430\u0444\u0438\u043a, \u043e\u0442\u0440\u0430\u0436\u0430\u044e\u0449\u0438\u0439 \u043a\u043e\u043b\u0435\u0431\u0430\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u044c \u0438 \u043f\u0440\u043e\u0434\u0430\u0432\u0430\u0442\u044c;  <\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043f\u0438\u0442\u0430\u043b, \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0439 \u0438\u0433\u0440\u043e\u043a\u0443 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0438\u0433\u0440\u044b.<\/p>\n<\/li>\n<\/ol>\n<h3>3.1. \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430<\/h3>\n<p>\u0418\u0442\u0430\u043a, \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c:<\/p>\n<ol>\n<li>\n<p>Ruby \u0438 Ruby on Rails (7 \u0432\u0435\u0440\u0441\u0438\u044f);<\/p>\n<\/li>\n<li>\n<p>Redis;<\/p>\n<\/li>\n<li>\n<p>Ngrok;<\/p>\n<\/li>\n<li>\n<p>PostgreSQL;<\/p>\n<\/li>\n<li>\n<p>npm \u0438 Flowbite.<\/p>\n<\/li>\n<\/ol>\n<p>\u0417\u0430\u0432\u0435\u0441\u0442\u0438 Telegram-\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/t.me\/BotFather\" rel=\"noopener noreferrer nofollow\">BotFather<\/a> \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0435\u0433\u043e API \u043a\u043b\u044e\u0447.<\/p>\n<h3>3.2. \u041e\u0431\u0449\u0438\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b<\/h3>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e rails 7.0.8, ruby 3.3.1, \u043e\u0434\u043d\u0430\u043a\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b.<\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f rails \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 postgresql:<\/p>\n<p><code>rails new investment_game --database=postgresql<\/code><\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 \u043f\u0430\u043f\u043a\u0443:<\/p>\n<p><code>cd investment_game<\/code><\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0432 Gemfile:<\/p>\n<pre><code class=\"ruby\">gem \"mutex_m\" gem \"telegram-bot-ruby\" gem \"redis-rails\" gem \"dotenv-rails\" gem \"tailwindcss-rails\" gem \"hotwire-rails\"<\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c bundle, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<p><code>bundle<\/code><\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u0434:<\/p>\n<p><code>rails db:create<\/code> <\/p>\n<p>\u0414\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 ngrok \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 app\/config\/environments\/development.rb \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443:<\/p>\n<p><code>config.hosts &lt;&lt; \/.*.ngrok-free.app\/<\/code><\/p>\n<p>\u0414\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043d\u0430\u043c \u0444\u0438\u0447, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u044e\u0437\u0435\u0440\u0430(\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 <code>--skip-test-framework<\/code> &#8212; \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b):<\/p>\n<p><code>rails g model User --skip-test-framework<\/code> <\/p>\n<p>\u0412 db\/migrate\/ \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f <code>XXXXXXXXXXXXX_create_users.rb<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"ruby\">class CreateUsers &lt; ActiveRecord::Migration[7.0]   def change     create_table :users do |t|       t.string  :name       t.string  :username       t.bigint  :telegram_id       t.decimal :capital, precision: 10, scale: 2, default: 10000.to_d       t.integer :amount_of_tokens        t.timestamps     end   end end <\/code><\/pre>\n<p>\u041f\u0440\u043e\u0433\u043e\u043d\u044f\u0435\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e<\/p>\n<p><code>rails db:migrate<\/code><\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0431\u043e\u0442\u0430, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b .env \u0434\u043b\u044f \u0438\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f, \u043f\u043e\u0447\u0442\u0438 \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u0432\u0430\u0448\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043d\u0430\u0448\u0438\u0445, \u043f\u043e\u0442\u043e\u043c\u0443 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0438\u0445 \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0437\u0430\u043d\u043e\u0441\u0438\u0442\u044c \u0438\u0445 \u0432 .env \u0444\u0430\u0439\u043b. \u0422\u0430\u043a\u0436\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e NGROK_URL \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c. \u0418\u0442\u0430\u043a, \u043d\u0430\u0448 <code>.env<\/code> \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c:<\/p>\n<pre><code>REDIS_URL = 'redis:\/\/localhost:6379\/0' TOKEN = 'TOKEN' NGROK_URL = 'NGROK_URL'<\/code><\/pre>\n<h3>3.3. Redis, \u0431\u043e\u0442, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 telegram mini app<\/h3>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0432\u0443\u0447\u0438\u0442 \u0442\u0430\u043a: &#171;\u041d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0431\u043e\u0442\u0430&#187;. \u0427\u0442\u043e \u0436\u0435, \u0445\u043e\u0440\u043e\u0448\u043e. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u043e\u043f\u043b\u043e\u0449\u0430\u0442\u044c.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b <code>redis.rb<\/code> \u0432 <code>config\/initializers\/<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0443\u0434\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438(\u0434\u0430\u043b\u0435\u0435 \u044f \u0431\u0443\u0434\u0443 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043a\u043e\u0434 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438, \u0431\u043e\u043b\u044c\u0448\u0435 \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u043c\u0438 \u0441\u0443\u0442\u044c):<\/p>\n<pre><code class=\"ruby\"># \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c Redis, \u0432\u0430\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e  # \u0435\u0433\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0438  \u0443\u0437\u043d\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441. \u0427\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441  # \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443: 'sudo netstat -tlnp | grep redis' REDIS = Redis.new(url: ENV['REDIS_URL']) <\/code><\/pre>\n<p>\u041f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0432\u043e\u043f\u043b\u043e\u0449\u0435\u043d\u0438\u044e, \u043d\u0430\u0448 \u0431\u043e\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e\u0442 telegram api, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044f\u0441\u044c \u043a \u043d\u0435\u043c\u0443, \u0432\u044b \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0434\u043b\u044f \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043d\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u043e\u0442 API \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>bot<\/code> \u0438 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <code>bot.rb<\/code>:<\/p>\n<pre><code class=\"ruby\"># \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 require File.expand_path('..\/config\/environment', __dir__) require 'telegram\/bot'  class TelegramBot   def initialize     # \u041f\u0440\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0430\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c API \u0442\u043e\u043a\u0435\u043d \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e      # \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432 \u0442\u0433 \u0441\u043e\u0437\u0434\u0430\u0432 \u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e https:\/\/t.me\/BotFather     @bot = Telegram::Bot::Client.new(ENV['TOKEN'])   end    def run     # \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043c\u044b \"\u043b\u043e\u0432\u0438\u043c\" \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0442\u0438\u043f\u044b      # \u0430\u043f\u0434\u0435\u0439\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0439\u0442\u0438 \u043a \u043d\u0430\u043c \u043e\u0442 \u0422\u0413     @bot.listen do |update|       case update       when Telegram::Bot::Types::Message         handle_message(update)       when Telegram::Bot::Types::CallbackQuery         handle_callback_query(update)       when Telegram::Bot::Types::ChatMemberUpdated         handle_chat_member_updated(update)       else         puts \"\u041d\u0435\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430: #{update.class}\"       end     end   end    private    def handle_message(message)     case message.text     when '\/start'       start_command(message)     when '\/stop'       stop_command(message)     else       handle_unknown_command(message)     end   end    def handle_callback_query(callback_query)     puts \"\u041f\u043e\u043b\u0443\u0447\u0435\u043d callback query: #{callback_query.data}\"   end    def handle_chat_member_updated(chat_member_updated)     puts \"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f: #{chat_member_updated.from.id}\"   end    def start_command(message)     # \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u0442\u043e\u043a\u0435\u043d\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c      # \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u0441 \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043d\u0430\u0448      # \u0441\u0435\u0440\u0432\u0438\u0441. \u0412\u0430\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u043c\u044b \u043a\u043b\u0430\u0434\u0451\u043c \u0432      # \u0420\u0435\u0434\u0438\u0441, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0442\u043e\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f      auth_token = generate_auth_token(message)     webapp_url = \"#{ENV['NGROK_URL']}?tg_token=#{auth_token}\"          keyboard = Telegram::Bot::Types::InlineKeyboardMarkup.new(       inline_keyboard: [         [           Telegram::Bot::Types::InlineKeyboardButton.new(             text: 'Investment game app',             web_app: { url: webapp_url }           )         ]       ]     )      @bot.api.send_message(       chat_id: message.chat.id,       text: \"\u0418\u0433\u0440\u0430\u0442\u044c \u0432 \u043e\u0434\u0438\u043d \u043a\u043b\u0438\u043a!\",       reply_markup: keyboard     )   end    def stop_command(message)     @bot.api.send_message(chat_id: message.chat.id, text: \"\u0414\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f, #{message.from.first_name}!\")   end    def handle_unknown_command(message)     @bot.api.send_message(chat_id: message.chat.id, text: \"\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430.\")   end    def generate_auth_token(message)     token = SecureRandom.hex(16)     user_info = {       telegram_id: message.from.id,       name: message.from.first_name,       username: message.from.username     }      REDIS.set(token, user_info.to_json)     token   end end  bot = TelegramBot.new bot.run <\/code><\/pre>\n<p> \u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f middleware, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c HTTP-\u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043e\u043d \u043f\u043e\u043f\u0430\u0434\u0451\u0442 \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440. \u0414\u0435\u043b\u0430\u0435\u043c \u043c\u044b \u044d\u0442\u043e, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0438 \u0432 \u043f\u043e\u043b\u043d\u043e\u0439 \u043c\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 middleware. <\/p>\n<p>\u0412 <code>app\/<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>middleware\/<\/code> \u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0451 <code>telegram_auth.rb<\/code><\/p>\n<pre><code class=\"ruby\">class TelegramAuth   def initialize(app)     @app = app   end    def call(env)     # \u0417\u0434\u0435\u0441\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f redis \u043c\u044b \u043d\u0430\u0445\u043e\u0434\u0438\u043c \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u043c      # \u043f\u043e \u0442\u043e\u043a\u0435\u043d\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0435\u0433\u043e user_id \u0432 \u0441\u0435\u0441\u0441\u0438\u044e      request = Rack::Request.new(env)      token = request.params['tg_token']      if token.present?       user_info_json = REDIS.get(token)       user_info = JSON.parse(user_info_json)       user = User.find_or_initialize_by(telegram_id: user_info['telegram_id'])       user.update(         name: user_info['name'],         username: user_info['username']       )       env['rack.session'][:user_id] = user.id       REDIS.del(token)       return [302, {'Location' => '\/'}, []]     else       puts \"Some error\"     end          @app.call(env)   end end <\/code><\/pre>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0434\u043b\u044f middleware \u0432 <code>config\/application.rb<\/code>, \u0432\u043e\u0442 \u043a\u0430\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c <code>application.rb<\/code> :<\/p>\n<pre><code class=\"ruby\">require_relative \"boot\" require_relative '..\/app\/middleware\/telegram_auth' require \"rails\/all\"  # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups)  module InvestmentGame   class Application &lt; Rails::Application     # Initialize configuration defaults for originally generated Rails version.     config.load_defaults 7.0      # Configuration for the application, engines, and railties goes here.     #     # These settings can be overridden in specific environments using the files     # in config\/environments, which are processed later.     #     # config.time_zone = \"Central Time (US &amp; Canada)\"     # config.eager_load_paths &lt;&lt; Rails.root.join(\"extras\")          config.middleware.use TelegramAuth   end end <\/code><\/pre>\n<p>\u0418\u0437\u043c\u0435\u043d\u0438\u043c \u0440\u0443\u0442\u044b:<\/p>\n<pre><code class=\"ruby\">Rails.application.routes.draw do   root 'users#index'   get 'users\/new', to: 'users#new', as: :new_user end <\/code><\/pre>\n<p>\u0412\u043d\u0435\u0441\u0451\u043c \u0432 <code>app\/controllers\/application_controller.rb<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f<\/p>\n<pre><code class=\"ruby\">class ApplicationController &lt; ActionController::Base   helper_method :current_user    def current_user     @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]   end end <\/code><\/pre>\n<p>\u0412 <code>app\/controllers<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <code>users_controller.rb<\/code> \u0438 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u044d\u043a\u0448\u0435\u043d index:<\/p>\n<pre><code class=\"ruby\">class UsersController &lt; ApplicationController   def index     @user = current_user   end end <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043d\u0443\u0436\u043d\u043e\u0435 view, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u043b\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f. \u0412 <code>app\/views\/<\/code> \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0430\u043f\u043a\u0443 <code>users<\/code> \u0438 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <code>index.html.erb<\/code> \u043d\u0430\u043f\u043e\u043b\u043d\u0438\u0432 \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043a\u043e\u0434\u043e\u043c:<\/p>\n<pre><code class=\"xml\">&lt;style>   body {     background-color: white;    } &lt;\/style>  &lt;h1>\u041f\u0440\u043e\u0444\u0438\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f&lt;\/h1>  &lt;% if @user %>   &lt;p>ID: &lt;%= @user.telegram_id %>&lt;\/p>   &lt;p>\u0418\u043c\u044f: &lt;%= @user.name || '\u041d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e' %>&lt;\/p>   &lt;p>\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f: &lt;%= @user.username || '\u041d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e' %>&lt;\/p> &lt;% else %>   &lt;p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d&lt;\/p> &lt;% end %> <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c, \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 ngrok \u0438 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u0431\u043e\u0442\u0430:<\/p>\n<p>\u041f\u0435\u0440\u0432\u043e\u0439 \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0440\u0435\u043b\u044c\u0441\u0443:<\/p>\n<p><code>rails s<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1b4\/bbb\/c44\/1b4bbbc441c6979739601c1a09da4cac.png\" alt=\"\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u0430\u044f \u043d\u0430 3000 \u0440\u0435\u043b\u044c\u0441\u0430\" title=\"\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u0430\u044f \u043d\u0430 3000 \u0440\u0435\u043b\u044c\u0441\u0430\" width=\"664\" height=\"226\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1b4\/bbb\/c44\/1b4bbbc441c6979739601c1a09da4cac.png\"\/><\/p>\n<div><figcaption>\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u0430\u044f \u043d\u0430 3000 \u0440\u0435\u043b\u044c\u0441\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043d\u0430 3000 \u043f\u043e\u0440\u0442\u0443, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438 ngrok \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430 \u044d\u0442\u043e\u0442 \u043f\u043e\u0440\u0442, \u0437\u0430\u043f\u0443\u0441\u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u0432 \u043d\u043e\u0432\u043e\u0439 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430(\u0435\u0441\u043b\u0438 \u0432\u044b \u0432 \u0443\u0431\u0443\u043d\u0442\u0443):<\/p>\n<p><code>ngrok http http:\/\/localhost:3000<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/655\/dd8\/387\/655dd83872bc8aa8a1a411c40b874644.png\" alt=\"\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 ngrok\" title=\"\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 ngrok\" width=\"989\" height=\"184\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/655\/dd8\/387\/655dd83872bc8aa8a1a411c40b874644.png\"\/><\/p>\n<div><figcaption>\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 ngrok<\/figcaption><\/div>\n<\/figure>\n<p>Ngrok \u0437\u0430\u043f\u0443\u0449\u0435\u043d, \u0430 \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 \u0441\u0435\u0442\u0438 \u043f\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443<\/p>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0432  <code>.env<\/code> \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0430\u0434\u0440\u0435\u0441 ngrok \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u0431\u043e\u0442\u0430:<\/p>\n<pre><code>REDIS_URL = 'redis:\/\/localhost:6379\/0' TOKEN = '6294394111:AAGm2qRHrd9GGms7PwZxVUL0DkQ4mFVPWIQ' NGROK_URL = 'https:\/\/cbf3-188-169-10-159.ngrok-free.app'<\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0438 \u0432 \u0441\u043e\u0441\u0435\u0434\u043d\u0435\u0439 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0431\u043e\u0442\u0430:<\/p>\n<p><code>ruby bot\/bot.rb<\/code> <\/p>\n<p>\u041f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u043e\u0442\u0430 \u0438 \u0432\u0432\u043e\u0434\u0438\u043c <code>\/start<\/code>  \u0432 \u0447\u0430\u0442\u0435 \u0441 \u043d\u0438\u043c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2ba\/53c\/ead\/2ba53ceadc1423978e429e10b40753e6.png\" alt=\"\u041e\u043d \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043d\u0430\u043c \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435\" title=\"\u041e\u043d \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043d\u0430\u043c \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435\" width=\"867\" height=\"1019\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2ba\/53c\/ead\/2ba53ceadc1423978e429e10b40753e6.png\"\/><\/p>\n<div><figcaption>\u041e\u043d \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043d\u0430\u043c \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/figcaption><\/div>\n<\/figure>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1a2\/1b9\/601\/1a21b96013f37a3e385aa04acd6cdaec.png\" alt=\"\u041d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u0432 \u0441\u0435\u0442\u0438\" title=\"\u041d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u0432 \u0441\u0435\u0442\u0438\" width=\"463\" height=\"741\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a2\/1b9\/601\/1a21b96013f37a3e385aa04acd6cdaec.png\"\/><\/p>\n<div><figcaption>\u041d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u0432 \u0441\u0435\u0442\u0438<\/figcaption><\/div>\n<\/figure>\n<p>\u0416\u043c\u0451\u043c \u043f\u043e &#171;Visit Site&#187; \u0438 \u0432\u0443\u0430\u043b\u044f, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043c\u0438\u043d\u0438 \u044d\u043f\u043f \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/914\/081\/250\/914081250c7a9c89b6ef5e6106a71db7.png\" alt=\"\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\" title=\"\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\" width=\"478\" height=\"736\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/914\/081\/250\/914081250c7a9c89b6ef5e6106a71db7.png\"\/><\/p>\n<div><figcaption>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/figcaption><\/div>\n<\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0439\u0442\u0435, \u0447\u0442\u043e \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044f \u0431\u043e\u0442\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0440\u0435\u043b\u044c\u0441\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441, \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c ngrok, \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f .env \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0431\u043e\u0442\u0430.<\/p>\n<p>\u041f\u043e\u043a\u0430 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440, ngrok \u0438 \u0431\u043e\u0442\u0430 \u0438 \u043f\u043e\u0439\u0434\u0435\u043c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<h3>3.4. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0438 \u0438\u0433\u0440\u044b<\/h3>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0433\u0440\u043e\u0432\u043e\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0438 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u0440\u044b\u043d\u043a\u0430 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438.<\/p>\n<p>\u041d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0440\u043e\u0443\u0442\u0438\u043d\u0433, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c: <code>rails g model Market --skip-test-framework<\/code><\/p>\n<p>\u0418\u0437\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"ruby\">class CreateMarkets &lt; ActiveRecord::Migration[7.0]   def change     create_table :markets do |t|       t.date        :current_date       t.decimal     :price, precision: 10, scale: 2       t.references  :user, null: true, foreign_key: true       t.json        :price_history, default: []        t.timestamps     end   end end <\/code><\/pre>\n<p>\u0421\u043d\u043e\u0432\u0430 \u043f\u0440\u043e\u0433\u043e\u043d\u044f\u0435\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e: <code>rails db:migrate<\/code><\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 User \u0438 Market:<\/p>\n<p>\u0412 \u043c\u043e\u0434\u0435\u043b\u0438 Market \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0441\u0432\u044f\u0437\u044c \u0441 User:<\/p>\n<p><code>belongs_to :user<\/code><\/p>\n<p>\u0410 \u043c\u043e\u0434\u0435\u043b\u044c User \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u043b\u044c\u043d\u0435\u0435:<\/p>\n<pre><code class=\"ruby\">class User &lt; ApplicationRecord   # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0441\u0432\u044f\u0437\u044c \u0441 \u043c\u043e\u0434\u0435\u043b\u044c\u044e Market   has_one  :market, dependent: :destroy    private    def create_market     Market.create(user: self, current_date: 5.years.ago, price: 10)   end end <\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0431\u0434 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0430, \u043c\u043e\u0434\u0435\u043b\u0438 \u0433\u043e\u0442\u043e\u0432\u044b, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u043e\u0443\u0442\u0438\u043d\u0433 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u0444\u0430\u0439\u043b <code>config\/routes<\/code>, \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"ruby\">Rails.application.routes.draw do   root 'markets#show'   get 'users\/new', to: 'users#new', as: :new_user    resources :markets, only: [:show] do     member do       post 'buy', to: \"markets#buy\"       post 'sell', to: \"markets#sell\"       get 'next_date', to: \"markets#next_date\"     end   end end <\/code><\/pre>\n<p>\u0414\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0438\u0433\u0440\u044b \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0432\u0441\u0435\u0433\u043e 3 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f: \u043a\u0443\u043f\u0438\u0442\u044c, \u043f\u0440\u043e\u0434\u0430\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u043c\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0434\u0435\u043d\u044c.<\/p>\n<p>\u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 <code>app\/controllers<\/code> \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 <code>users_controller.rb<\/code> \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"ruby\">class MarketsController &lt; ApplicationController   before_action :set_market, only: [:show, :buy, :sell, :next_date]    def show   end    def buy         current_user.buy_tokens(params[:amount_of_dollars_for_buying], @market)      redirect_to market_path(@market), notice: 'Transaction completed successfully.'   end    def sell     current_user.sell_tokens(params[:amount_of_tokens_for_selling], @market)      redirect_to market_path(@market), notice: 'Transaction completed successfully.'   end    def next_date     @market.calculate_next_date      redirect_to market_path(@market), notice: \"Today's date is #{@market.current_date}\"   end    private    def set_market     @market = current_user.market   end end <\/code><\/pre>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0438\u043c\u0435\u043d\u043d\u043e \u044e\u0437\u0435\u0440 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442 \u043f\u043e\u043a\u0443\u043f\u043a\u0443, \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u043a\u043e\u0434 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0439 \u0441 \u043a\u0443\u043f\u043b\u0435\u0439\/\u043f\u0440\u043e\u0434\u0430\u0436\u0435\u0439 \u0432 \u043c\u043e\u0434\u0435\u043b\u044c \u044e\u0437\u0435\u0440\u0430<\/p>\n<p><code>app\/models\/user.rb:<\/code><\/p>\n<pre><code class=\"ruby\">def buy_tokens(amount_of_dollars, market)   tokens_to_buy = amount_of_dollars \/ market.price   if capital >= amount_of_dollars     update(capital: capital - amount_of_dollars, amount_of_tokens: amount_of_tokens + tokens_to_buy)   else     flash[:alert] = 'Not enough capital to buy tokens.'   end end  def sell_tokens(amount_of_dollars, market)   dollars_to_receive = amount_of_dollars * market.price   if amount_of_tokens >= amount_of_dollars     update(capital: capital + dollars_to_receive, amount_of_tokens: amount_of_tokens - amount_of_dollars)   else     flash[:alert] = 'Not enough tokens to sell.'   end end <\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u043c\u043e\u0442\u043a\u0430 \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0434\u0435\u043d\u044c \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u043d\u0435 \u0436\u0434\u0430\u0442\u044c \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0446\u0435\u043d\u044b. \u041c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u043c\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0443\u0442\u043a\u0438 \u0432\u043f\u0435\u0440\u0435\u0434 \u0438 \u0432\u044b\u0434\u0430\u0451\u043c \u0438\u0433\u0440\u043e\u043a\u0443 \u0446\u0435\u043d\u0443 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043d\u0443\u0436\u043d\u043e\u0439 \u043d\u0430\u043c \u0444\u043e\u0440\u043c\u0443\u043b\u043e\u0439. <\/p>\n<pre><code class=\"ruby\">class Market &lt; ApplicationRecord   belongs_to :user    def calculate_next_date     new_date = current_date + 1.day      new_price = VolatilityService.simulate      update(current_date: new_date, price: new_price)     price_history &lt;&lt; { date: new_date.strftime('%d %b'), price: new_price.to_f }     save   end end <\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u043c\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0446\u0435\u043d\u044b \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0435\u043d\u043d\u043e\u0435 \u043a \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044e \u0438 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u0432\u043e\u043b\u0430\u0442\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u0442\u0440\u0435\u043d\u0434. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 <code>app<\/code> \u043f\u0430\u043f\u043a\u0443 <code>services<\/code> \u0438 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <code>volatility_service.rb<\/code><\/p>\n<pre><code class=\"ruby\">module VolatilityService   def self.simulate(volatility_coefficient = 0.1, price = 100, trend = 0.001, time_step = 1)         drift = trend * time_step     diffusion = volatility_coefficient * gaussian_distribution * Math.sqrt(time_step)     new_price = price * Math.exp(drift + diffusion)     new_price   end    # \u041c\u0435\u0442\u043e\u0434 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0447\u0438\u0441\u043b\u043e    # \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435\u043c   def self.gaussian_distribution     theta = 2 * Math::PI * rand     rho = Math.sqrt(-2 * Math.log(1 - rand))     scale = 0.4          scale * rho * Math.cos(theta)   end end <\/code><\/pre>\n<h3>3.5. \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c flowbite, tailwind \u0438 \u043f\u0438\u0448\u0435\u043c \u0432\u044c\u044e\u0445\u0438<\/h3>\n<p>\u0412 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 3.1 \u044f \u043f\u043e\u0441\u043e\u0432\u0435\u0442\u043e\u0432\u0430\u043b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c npm \u0438 flowbite, \u043e\u043d\u0438 \u043d\u0443\u0436\u043d\u044b \u043d\u0430\u043c \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430, \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438 \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u0432\u044c\u044e\u0445, \u0441\u0442\u0438\u043b\u0435\u0439 \u0438 js. \u0414\u0430\u043b\u0435\u0435 \u044f \u043e\u043f\u0438\u0448\u0443 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438.<\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c tailwind:<\/p>\n<p><code>.\/bin\/rails tailwindcss:install<\/code><\/p>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f flowbite, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u0431\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043d\u0430 \u0431\u0430\u0437\u0435 tailwind, \u043e\u0447\u0435\u043d\u044c \u043a\u0440\u0443\u0442\u0430\u044f \u0432\u0435\u0449\u044c \u0435\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0444\u0440\u043e\u043d\u0442\u043e\u0432\u0443\u044e \u0447\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c flowbite \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>npm install flowbite<\/code> <\/p>\n<p>\u0412 <code>config\/tailwind.config.js<\/code> \u0432 plugins \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c flowbite: <code>require('flowbite\/plugin')<\/code><\/p>\n<p>\u0412 content \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <code>.\/node_modules\/flowbite\/**\/*.js'<\/code><br \/>\u041d\u0430\u0448 <code>tailwind.config.js<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"ruby\">const defaultTheme = require('tailwindcss\/defaultTheme')  module.exports = {   content: [     '.\/public\/*.html',     '.\/app\/helpers\/**\/*.rb',     '.\/app\/javascript\/**\/*.js',     '.\/app\/views\/**\/*.{erb,haml,html,slim}',     '.\/node_modules\/flowbite\/**\/*.js'   ],   theme: {     extend: {       fontFamily: {         sans: ['Inter var', ...defaultTheme.fontFamily.sans],       },     },   },   plugins: [     require('@tailwindcss\/forms'),     require('@tailwindcss\/typography'),     require('@tailwindcss\/container-queries'),     require('flowbite\/plugin')({       charts: true,     }),   ] } <\/code><\/pre>\n<p>\u0412 <code>config\/importmap.rb<\/code> \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code>pin \"flowbite\", to: \"https:\/\/cdn.jsdelivr.net\/npm\/flowbite@2.4.1\/dist\/flowbite.turbo.min.js\"<\/code><\/pre>\n<p>\u0412 app\/javascript\/application.js \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c: <code>import 'flowbite';<\/code> <\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0430\u043f\u043a\u0443 <code>markets<\/code> \u0438 <code>show.html.erb<\/code> \u0432 <code>app\/view\/markets<\/code> . \u0412\u0435\u0440\u0441\u0442\u043a\u0443 \u0438 \u0433\u0440\u0430\u0444\u0438\u043a \u043c\u044b \u0432\u0437\u044f\u043b\u0438 <a href=\"https:\/\/medium.com\/themesberg-blog\/open-source-chart-components-built-with-tailwind-css-flowbite-and-apexcharts-18c13ede5b7c\" rel=\"noopener noreferrer nofollow\">\u043e\u0442\u0441\u044e\u0434\u0430<\/a><\/p>\n<pre><code class=\"xml\">&lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/apexcharts\">&lt;\/script> &lt;div class=\"container\">   &lt;div class=\"chart-container\">     &lt;div id=\"chart-data\"         data-prices=\"&lt;%= @market.price_history.last(10).map { |entry| entry['price'] }.join(',') %>\"         data-dates=\"&lt;%= @market.price_history.last(10).map { |entry| entry['date'] }.join(',') %>\">     &lt;\/div>     &lt;div id=\"line-chart\">&lt;\/div>   &lt;\/div>    &lt;div class=\"mt-4 mx-auto max-w-md\">     &lt;h2 class=\"text-xl font-bold\">Username: &lt;%= current_user.username %>&lt;\/h2>     &lt;p class=\"text-lg\">Today date: &lt;%= @market.current_date.strftime('%Y-%m-%d') %>&lt;\/p>     &lt;p class=\"text-lg\">Price: &lt;%= @market.price %>&lt;\/p>      &lt;div class=\"grid grid-cols-2 gap-4\">       &lt;div>         &lt;%= form_with url: buy_market_path(@market), method: :post, local: true do |form| %>           &lt;%= form.label :amount_of_dollars_for_buying, \"Amount of dollars:\", class: 'block text-base font-medium mb-2' %>           &lt;%= form.number_field :amount_of_dollars_for_buying, step: 'any', class: 'w-full bg-gray-800 text-white h-10 px-3 rounded mb-2' %>           &lt;%= form.submit \"Buy Tokens\", class: 'btn btn-green' %>         &lt;% end %>       &lt;\/div>        &lt;div>         &lt;%= form_with url: sell_market_path(@market), method: :post, local: true do |form| %>           &lt;%= form.label :amount_of_tokens_for_selling, \"Amount of tokens:\", class: 'block text-base font-medium mb-2' %>           &lt;%= form.number_field :amount_of_tokens_for_selling, step: 'any', class: 'w-full bg-gray-800 text-white h-10 px-3 rounded mb-2' %>           &lt;%= form.submit \"Sell Tokens\", class: 'btn btn-red' %>         &lt;% end %>       &lt;\/div>     &lt;\/div>   &lt;\/div>    &lt;div id=\"user-info\" class=\"mt-4 mx-auto max-w-md\">     &lt;p class=\"text-base\">You have: &lt;%= number_with_precision(current_user.capital, precision: 2) %> dollars&lt;\/p>     &lt;p class=\"text-base\">You have: &lt;%= current_user.amount_of_tokens %> tokens&lt;\/p>   &lt;\/div>    &lt;div class=\"mt-4 w-full max-w-md px-4\">     &lt;%= link_to 'Next Day', next_date_market_path(@market), class: 'btn btn-lime' %>   &lt;\/div> &lt;\/div> &lt;script>   function initializeChart() {     const chartDataElement = document.getElementById(\"chart-data\");     const prices = chartDataElement.getAttribute(\"data-prices\").split(',').map(Number);     const dates = chartDataElement.getAttribute(\"data-dates\").split(',');      const options = {       series: [{         name: \"Price\",         data: prices,         color: \"#00FF00\",       }],       chart: {         height: 300,         type: \"area\",         fontFamily: \"Inter, sans-serif\",         dropShadow: {           enabled: false,         },         toolbar: {           show: false,         },       },       dataLabels: {         enabled: true,         style: {           cssClass: 'text-xs text-white font-medium',           fontSize: '9px',           colors: ['#049804'],         },         formatter: function (value) {           return value.toFixed(2);         }       },       stroke: {         width: 2,       },       grid: {         show: true,         strokeDashArray: 4,         padding: {           left: 16,           right: 16,           top: 0,         },       },       tooltip: {         enabled: true,         x: {           show: false,         },       },       fill: {         type: \"gradient\",         gradient: {           opacityFrom: 0.55,           opacityTo: 0,           shade: \"#00FF00\",           gradientToColors: [\"#00FF00\"],         },       },       xaxis: {         categories: dates,         labels: {           show: true,           style: {             colors: '#FFFFFF',             fontFamily: \"Inter, sans-serif\",             cssClass: \"text-xs font-normal\",           },         },         axisBorder: {           show: false,         },         axisTicks: {           show: false,         },       },       yaxis: {         show: false,       },     };      const chartElement = document.querySelector(\"#line-chart\");     if (chartElement) {       chartElement.innerHTML = '';        const chart = new ApexCharts(chartElement, options);       chart.render();     }   }    document.addEventListener(\"DOMContentLoaded\", initializeChart); &lt;\/script> <\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0431\u0438\u043b\u0434: <code>bin\/dev<\/code> , \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c ngrok, \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0438 \u0441\u043d\u043e\u0432\u0430 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0431\u043e\u0442\u0430. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dd4\/7fa\/239\/dd47fa23965f50c59922ae50e818ccc7.png\" alt=\"\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442!\" title=\"\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442!\" width=\"459\" height=\"739\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dd4\/7fa\/239\/dd47fa23965f50c59922ae50e818ccc7.png\"\/><\/p>\n<div><figcaption>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442!<\/figcaption><\/div>\n<\/figure>\n<h3>4. \u041f\u043e\u0441\u043b\u0435\u0441\u043b\u043e\u0432\u0438\u0435, \u043a\u0430\u043a \u0438 \u043a\u043e\u043c\u0443 \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f<\/h3>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 mini-app \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f \u0438 \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0430\u044f\u0441\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u0444\u0435\u0440\u0430 \u0432\u0435\u0431 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u043e. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f, \u0447\u0442\u043e telegram-mini app \u044d\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u043d\u0430\u044f &#171;\u0431\u0430\u0437\u0430&#187; \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u043e\u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0436\u0438\u0437\u043d\u0435\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432(MVP). <\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0438\u0437 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0438 \u0431\u044d\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432, \u043d\u043e \u0437\u0430\u0447\u0435\u043c, \u0435\u0441\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c Telegram Mini App, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c 1 \u0432 1 \u043a\u0430\u043a \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0444\u0443\u043b\u0441\u0442\u0435\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c\u0438. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0430 \u0432\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0434\u0435\u0448\u0435\u0432\u043b\u0435, \u043f\u0440\u043e\u0434\u0443\u043a\u0442 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0430\u0443\u0434\u0438\u0442\u043e\u0440\u0438\u0438 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c.<\/p>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e \u044d\u0442\u043e \u043d\u0435 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0443\u044e \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438 \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043d\u044e\u0430\u043d\u0441\u044b, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043c\u0438\u043d\u0443\u0441\u044b. \u041d\u043e \u043e\u0431 \u044d\u0442\u043e\u043c \u043a\u0430\u043a-\u043d\u0438\u0431\u0443\u0434\u044c \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0440\u0430\u0437.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043f\u0440\u0438 \u0443\u0447\u0430\u0441\u0442\u0438\u0438<br \/><a href=\"https:\/\/t.me\/mike_godunoff\" rel=\"noopener noreferrer nofollow\">\u0413\u043e\u0434\u0443\u043d\u043e\u0432 \u041c\u0438\u0445\u0430\u0438\u043b<\/a><br \/><a href=\"https:\/\/t.me\/DanilLetyaga\" rel=\"noopener noreferrer nofollow\">\u041b\u0435\u0442\u044f\u0433\u0430 \u0414\u0430\u043d\u0438\u043b<\/a><br \/><a href=\"https:\/\/t.me\/George_Target\" rel=\"noopener noreferrer nofollow\">\u0411\u0430\u0435\u0432 \u0413\u0435\u043e\u0440\u0433\u0438\u0439 <\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/829520\/\"> https:\/\/habr.com\/ru\/articles\/829520\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>1.1. \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043c\u0438\u043d\u0438 \u044d\u043f\u043f\u044b \u0438 \u0420\u0443\u0431\u0438<\/h3>\n<p>\u0421 \u043d\u0435\u0434\u0430\u0432\u043d\u0438\u0445 \u043f\u043e\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c Telegram \u0441\u0438\u043b\u044c\u043d\u043e \u0432\u044b\u0440\u043e\u0441\u043b\u0430. \u041f\u043e\u043c\u0438\u043c\u043e \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u0431\u043e\u0442\u043e\u0432, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u044f\u0440\u043a\u043e \u0432\u044b\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f Telegram Mini Apps. \u0418\u0437\u0443\u0447\u0438\u0432, \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0443 \u0430\u0432\u0442\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0438\u0434\u0435\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u0432\u044b\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0437\u0438\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u043c.<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443, \u043f\u043e \u043c\u043d\u0435\u043d\u0438\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u044f\u0437\u044b\u043a \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f Ruby \u0438 \u0435\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Ruby on Rails \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u0438\u043b\u0443\u0447\u0448\u0438\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432, \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u0422\u0430\u043a\u0436\u0435 \u0432\u0430\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043d\u0430 GitHub \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043c\u0430\u043b\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043a\u043e\u0434\u0430 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Mini App \u043d\u0430 Ruby. \u0412\u0441\u0435\u043c\u0438\u0440\u043d\u0430\u044f \u043f\u0430\u0443\u0442\u0438\u043d\u0430 \u0442\u043e\u0436\u0435 \u043d\u0435 \u043e\u0441\u043e\u0431\u043e \u0440\u0430\u0434\u043e\u0432\u0430\u043b\u0430 \u043d\u0430\u0441 \u0440\u0430\u0431\u043e\u0447\u0438\u043c\u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0451\u0439 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0432\u043d\u0435\u0441\u0442\u0438 \u0441\u0432\u043e\u0439 \u043f\u043e\u0441\u0438\u043b\u044c\u043d\u044b\u0439 \u0432\u043a\u043b\u0430\u0434 \u0432 \u0441\u0444\u0435\u0440\u0443, \u043a\u0430\u0441\u0430\u044e\u0449\u0443\u044e\u0441\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430 Ruby.<\/p>\n<h3>1.2. \u0424\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u0430\u044f \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e\u0441\u0442\u044c<\/h3>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u0432\u044f\u0442\u0438\u043b\u0438 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044e \u0441\u0444\u0435\u0440\u044b \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u044b\u0445 \u0430\u043a\u0442\u0438\u0432\u043e\u0432 \u0438 \u043a\u0440\u0438\u043f\u0442\u043e\u0432\u0430\u043b\u044e\u0442, \u043d\u0430\u043c\u0438 \u0431\u044b\u043b\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0433\u0440\u0443, \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u0443\u044e\u0449\u0443\u044e \u0440\u044b\u043d\u043e\u0447\u043d\u0443\u044e \u0432\u043e\u043b\u0430\u0442\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0434\u043d\u043e\u0439 \u0432\u044b\u0434\u0443\u043c\u0430\u043d\u043d\u043e\u0439 \u0430\u043a\u0446\u0438\u0438. \u041d\u0430\u0437\u043e\u0432\u0451\u043c \u0435\u0451 &#171;AVA&#187; (an volatile asset), \u0447\u0442\u043e \u043f\u043e-\u0440\u0443\u0441\u0441\u043a\u0438 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 &#171;\u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0439 \u0430\u043a\u0442\u0438\u0432&#187;.<\/p>\n<p>\u0417\u0430\u0447\u0435\u043c \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u044b\u043d\u043e\u0447\u043d\u0443\u044e \u0432\u043e\u043b\u0430\u0442\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u043f\u0440\u0438 \u0447\u0435\u043c \u0442\u0443\u0442 \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u0430\u044f \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e\u0441\u0442\u044c?<\/p>\n<p>\u0418\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440\u044b \u0414\u0436\u043e\u043d \u0411\u043e\u0433\u043b \u0438 \u0411\u0435\u043d\u0434\u0436\u0430\u043c\u0438\u043d \u0413\u0440\u044d\u043c \u0432 \u0441\u0432\u043e\u0438\u0445 \u0440\u0430\u0431\u043e\u0442\u0430\u0445 \u0432\u044b\u0434\u0432\u0438\u043d\u0443\u043b\u0438 \u0442\u0435\u0437\u0438\u0441\u044b \u043e \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430\u0445 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0434 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u0440\u0442\u0444\u0435\u043b\u0435\u043c. \u0412 \u043a\u043d\u0438\u0433\u0435 &#171;\u0420\u0430\u0437\u0443\u043c\u043d\u044b\u0439 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440&#187; \u0413\u0440\u044d\u043c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u0441\u043b\u043e\u0436\u043d\u043e\u0435 \u0438 \u0447\u0430\u0441\u0442\u043e \u043d\u0435\u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440\u043e\u0432 \u0438\u0437-\u0437\u0430 \u0432\u044b\u0441\u043e\u043a\u0438\u0445 \u0438\u0437\u0434\u0435\u0440\u0436\u0435\u043a \u0438 \u043d\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432. \u041e\u043d \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442 \u0438\u043d\u0432\u0435\u0441\u0442\u043e\u0440\u0430\u043c \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u0434\u043e\u043b\u0433\u043e\u0441\u0440\u043e\u0447\u043d\u044b\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u043d\u044b\u0435 \u0444\u043e\u043d\u0434\u044b \u0438\u043b\u0438 \u0434\u0438\u0432\u0435\u0440\u0441\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u0444\u0435\u043b\u0438 \u0430\u043a\u0446\u0438\u0439 \u0438 \u043e\u0431\u043b\u0438\u0433\u0430\u0446\u0438\u0439, \u0447\u0442\u043e \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0440\u0438\u0441\u043a\u0438 \u0438 \u0438\u0437\u0434\u0435\u0440\u0436\u043a\u0438. \u0411\u043e\u0433\u043b \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e \u0438\u043d\u0434\u0435\u043a\u0441\u043d\u044b\u0445 \u0444\u043e\u043d\u0434\u043e\u0432 \u043a\u0430\u043a \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u044f\u0441\u044c \u043e\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0448\u0438\u0440\u043e\u043a\u043e\u043c\u0443 \u0440\u044b\u043d\u043a\u0443 \u0441 \u043d\u0438\u0437\u043a\u0438\u043c\u0438 \u043a\u043e\u043c\u0438\u0441\u0441\u0438\u044f\u043c\u0438 \u0438 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0438\u0437\u0434\u0435\u0440\u0436\u043a\u0430\u043c\u0438.<\/p>\n<p>\u0420\u0435\u0437\u044e\u043c\u0438\u0440\u0443\u044f: \u043f\u043e \u043c\u043d\u0435\u043d\u0438\u044e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u0430\u0432\u0442\u043e\u0440\u043e\u0432, \u043f\u043e\u043f\u044b\u0442\u043a\u0438 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u0441\u043f\u0435\u043a\u0443\u043b\u044f\u0446\u0438\u044f\u0445 \u043d\u0430 \u0440\u044b\u043d\u043a\u0435 \u0446\u0435\u043d\u043d\u044b\u0445 \u0431\u0443\u043c\u0430\u0433 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u044b\u0433\u043e\u0434\u043d\u044b \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0434\u043e\u043b\u0433\u043e\u0441\u0440\u043e\u0447\u043d\u044b\u043c \u0438\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 &#171;\u043a\u0443\u043f\u0438\u043b \u0438 \u0437\u0430\u0431\u044b\u043b&#187;. \u041d\u0430\u0448\u0430 \u0438\u0433\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e. \u0427\u0442\u043e\u0431\u044b \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c \u0432 \u043d\u0435\u0439 \u0431\u043e\u043b\u044c\u0448\u0435, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043a\u0443\u043f\u0438\u0442\u044c \u0430\u043a\u0442\u0438\u0432 \u0438 \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0435\u0433\u043e, \u043d\u043e, \u043a\u0430\u043a \u0446\u0435\u043d\u0438\u0442\u0435\u043b\u0438 \u0430\u0437\u0430\u0440\u0442\u0430, \u043c\u044b \u0434\u0430\u0451\u043c \u0438\u0433\u0440\u043e\u043a\u0443 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0441\u043f\u0435\u043a\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u043a\u0430\u043a \u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0436\u0438\u0437\u043d\u0438, \u043d\u0430\u0439\u0434\u0443\u0442\u0441\u044f \u0441\u0447\u0430\u0441\u0442\u043b\u0438\u0432\u0447\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043c\u043e\u0433\u0443\u0442 \u0441\u043b\u043e\u0432\u0438\u0442\u044c \u0443\u0434\u0430\u0447\u0443 \u0438 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u0432\u0430\u043b\u044e\u0442\u0443, \u043e\u0434\u043d\u0430\u043a\u043e \u0441\u043f\u0435\u043a\u0443\u043b\u044f\u043d\u0442\u044b \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0447\u0430\u0441\u0442\u043e \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u044e\u0442 \u043d\u0430 \u0434\u043b\u0438\u043d\u043d\u044b\u0445 \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u044f\u0445.<\/p>\n<h3>2.1. \u0424\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438\u0433\u0440\u044b<\/h3>\n<p>\u041f\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0432\u044b\u0448\u0435, \u0438\u0433\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0438\u043c\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c: <\/p>\n<ol>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Telegram; <\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0434\u0430\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e &#171;\u043b\u0438\u0441\u0442\u0430\u0442\u044c&#187;, \u0442.\u0435. \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 &#171;\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0434\u0430\u0442\u0430&#187; \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0440\u043e\u043c\u043e\u0442\u0430\u0442\u044c\u0441\u044f \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 n \u0435\u0434\u0438\u043d\u0438\u0446 \u0432\u043f\u0435\u0440\u0435\u0434, \u0447\u0442\u043e\u0431\u044b \u0438\u0433\u0440\u043e\u043a \u043d\u0435 \u0436\u0434\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0446\u0435\u043d\u044b; <\/p>\n<\/li>\n<li>\n<p>\u0426\u0435\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u043a\u043e\u043b\u0435\u0431\u0430\u0442\u044c\u0441\u044f \u0434\u0435\u043d\u044c \u043e\u0442\u043e \u0434\u043d\u044f; <\/p>\n<\/li>\n<li>\n<p>\u0413\u0440\u0430\u0444\u0438\u043a, \u043e\u0442\u0440\u0430\u0436\u0430\u044e\u0449\u0438\u0439 \u043a\u043e\u043b\u0435\u0431\u0430\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u044c \u0438 \u043f\u0440\u043e\u0434\u0430\u0432\u0430\u0442\u044c;  <\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043f\u0438\u0442\u0430\u043b, \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0439 \u0438\u0433\u0440\u043e\u043a\u0443 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0438\u0433\u0440\u044b.<\/p>\n<\/li>\n<\/ol>\n<h3>3.1. \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430<\/h3>\n<p>\u0418\u0442\u0430\u043a, \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c:<\/p>\n<ol>\n<li>\n<p>Ruby \u0438 Ruby on Rails (7 \u0432\u0435\u0440\u0441\u0438\u044f);<\/p>\n<\/li>\n<li>\n<p>Redis;<\/p>\n<\/li>\n<li>\n<p>Ngrok;<\/p>\n<\/li>\n<li>\n<p>PostgreSQL;<\/p>\n<\/li>\n<li>\n<p>npm \u0438 Flowbite.<\/p>\n<\/li>\n<\/ol>\n<p>\u0417\u0430\u0432\u0435\u0441\u0442\u0438 Telegram-\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/t.me\/BotFather\" rel=\"noopener noreferrer nofollow\">BotFather<\/a> \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0435\u0433\u043e API \u043a\u043b\u044e\u0447.<\/p>\n<h3>3.2. \u041e\u0431\u0449\u0438\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b<\/h3>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e rails 7.0.8, ruby 3.3.1, \u043e\u0434\u043d\u0430\u043a\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b.<\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f rails \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 postgresql:<\/p>\n<p><code>rails new investment_game --database=postgresql<\/code><\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 \u043f\u0430\u043f\u043a\u0443:<\/p>\n<p><code>cd investment_game<\/code><\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0432 Gemfile:<\/p>\n<pre><code class=\"ruby\">gem \"mutex_m\" gem \"telegram-bot-ruby\" gem \"redis-rails\" gem \"dotenv-rails\" gem \"tailwindcss-rails\" gem \"hotwire-rails\"<\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c bundle, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<p><code>bundle<\/code><\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u0434:<\/p>\n<p><code>rails db:create<\/code> <\/p>\n<p>\u0414\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 ngrok \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 app\/config\/environments\/development.rb \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443:<\/p>\n<p><code>config.hosts &lt;&lt; \/.*.ngrok-free.app\/<\/code><\/p>\n<p>\u0414\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043d\u0430\u043c \u0444\u0438\u0447, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u044e\u0437\u0435\u0440\u0430(\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 <code>--skip-test-framework<\/code> &#8212; \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b):<\/p>\n<p><code>rails g model User --skip-test-framework<\/code> <\/p>\n<p>\u0412 db\/migrate\/ \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f <code>XXXXXXXXXXXXX_create_users.rb<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"ruby\">class CreateUsers &lt; ActiveRecord::Migration[7.0]   def change     create_table :users do |t|       t.string  :name       t.string  :username       t.bigint  :telegram_id       t.decimal :capital, precision: 10, scale: 2, default: 10000.to_d       t.integer :amount_of_tokens        t.timestamps     end   end end <\/code><\/pre>\n<p>\u041f\u0440\u043e\u0433\u043e\u043d\u044f\u0435\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e<\/p>\n<p><code>rails db:migrate<\/code><\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0431\u043e\u0442\u0430, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b .env \u0434\u043b\u044f \u0438\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f, \u043f\u043e\u0447\u0442\u0438 \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u0432\u0430\u0448\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043d\u0430\u0448\u0438\u0445, \u043f\u043e\u0442\u043e\u043c\u0443 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0438\u0445 \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0437\u0430\u043d\u043e\u0441\u0438\u0442\u044c \u0438\u0445 \u0432 .env \u0444\u0430\u0439\u043b. \u0422\u0430\u043a\u0436\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e NGROK_URL \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c. \u0418\u0442\u0430\u043a, \u043d\u0430\u0448 <code>.env<\/code> \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c:<\/p>\n<pre><code>REDIS_URL = 'redis:\/\/localhost:6379\/0' TOKEN = 'TOKEN' NGROK_URL = 'NGROK_URL'<\/code><\/pre>\n<h3>3.3. Redis, \u0431\u043e\u0442, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 telegram mini app<\/h3>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0432\u0443\u0447\u0438\u0442 \u0442\u0430\u043a: &#171;\u041d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0431\u043e\u0442\u0430&#187;. \u0427\u0442\u043e \u0436\u0435, \u0445\u043e\u0440\u043e\u0448\u043e. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u043e\u043f\u043b\u043e\u0449\u0430\u0442\u044c.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b <code>redis.rb<\/code> \u0432 <code>config\/initializers\/<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0443\u0434\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438(\u0434\u0430\u043b\u0435\u0435 \u044f \u0431\u0443\u0434\u0443 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043a\u043e\u0434 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438, \u0431\u043e\u043b\u044c\u0448\u0435 \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u043c\u0438 \u0441\u0443\u0442\u044c):<\/p>\n<pre><code class=\"ruby\"># \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c Redis, \u0432\u0430\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e  # \u0435\u0433\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0438  \u0443\u0437\u043d\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441. \u0427\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441  # \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443: 'sudo netstat -tlnp | grep redis' REDIS = Redis.new(url: ENV['REDIS_URL']) <\/code><\/pre>\n<p>\u041f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0432\u043e\u043f\u043b\u043e\u0449\u0435\u043d\u0438\u044e, \u043d\u0430\u0448 \u0431\u043e\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e\u0442 telegram api, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044f\u0441\u044c \u043a \u043d\u0435\u043c\u0443, \u0432\u044b \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0434\u043b\u044f \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043d\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u043e\u0442 API \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>bot<\/code> \u0438 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <code>bot.rb<\/code>:<\/p>\n<pre><code class=\"ruby\"># \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 require File.expand_path('..\/config\/environment', __dir__) require 'telegram\/bot'  class TelegramBot   def initialize     # \u041f\u0440\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0430\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c API \u0442\u043e\u043a\u0435\u043d \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e      # \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432 \u0442\u0433 \u0441\u043e\u0437\u0434\u0430\u0432 \u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e https:\/\/t.me\/BotFather     @bot = Telegram::Bot::Client.new(ENV['TOKEN'])   end    def run     # \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043c\u044b \"\u043b\u043e\u0432\u0438\u043c\" \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0442\u0438\u043f\u044b      # \u0430\u043f\u0434\u0435\u0439\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0439\u0442\u0438 \u043a \u043d\u0430\u043c \u043e\u0442 \u0422\u0413     @bot.listen do |update|       case update       when Telegram::Bot::Types::Message         handle_message(update)       when Telegram::Bot::Types::CallbackQuery         handle_callback_query(update)       when Telegram::Bot::Types::ChatMemberUpdated         handle_chat_member_updated(update)       else         puts \"\u041d\u0435\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430: #{update.class}\"       end     end   end    private    def handle_message(message)     case message.text     when '\/start'       start_command(message)     when '\/stop'       stop_command(message)     else       handle_unknown_command(message)     end   end    def handle_callback_query(callback_query)     puts \"\u041f\u043e\u043b\u0443\u0447\u0435\u043d callback query: #{callback_query.data}\"   end    def handle_chat_member_updated(chat_member_updated)     puts \"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f: #{chat_member_updated.from.id}\"   end    def start_command(message)     # \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u0442\u043e\u043a\u0435\u043d\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c      # \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u0441 \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043d\u0430\u0448      # \u0441\u0435\u0440\u0432\u0438\u0441. \u0412\u0430\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u043c\u044b \u043a\u043b\u0430\u0434\u0451\u043c \u0432      # \u0420\u0435\u0434\u0438\u0441, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0442\u043e\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f      auth_token = generate_auth_token(message)     webapp_url = \"#{ENV['NGROK_URL']}?tg_token=#{auth_token}\"          keyboard = Telegram::Bot::Types::InlineKeyboardMarkup.new(       inline_keyboard: [         [           Telegram::Bot::Types::InlineKeyboardButton.new(             text: 'Investment game app',             web_app: { url: webapp_url }           )         ]       ]     )      @bot.api.send_message(       chat_id: message.chat.id,       text: \"\u0418\u0433\u0440\u0430\u0442\u044c \u0432 \u043e\u0434\u0438\u043d \u043a\u043b\u0438\u043a!\",       reply_markup: keyboard     )   end    def stop_command(message)     @bot.api.send_message(chat_id: message.chat.id, text: \"\u0414\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f, #{message.from.first_name}!\")   end    def handle_unknown_command(message)     @bot.api.send_message(chat_id: message.chat.id, text: \"\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430.\")   end    def generate_auth_token(message)     token = SecureRandom.hex(16)     user_info = {       telegram_id: message.from.id,       name: message.from.first_name,       username: message.from.username     }      REDIS.set(token, user_info.to_json)     token   end end  bot = TelegramBot.new bot.run <\/code><\/pre>\n<p> \u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f middleware, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c HTTP-\u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043e\u043d \u043f\u043e\u043f\u0430\u0434\u0451\u0442 \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440. \u0414\u0435\u043b\u0430\u0435\u043c \u043c\u044b \u044d\u0442\u043e, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0438 \u0432 \u043f\u043e\u043b\u043d\u043e\u0439 \u043c\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 middleware. <\/p>\n<p>\u0412 <code>app\/<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>middleware\/<\/code> \u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0451 <code>telegram_auth.rb<\/code><\/p>\n<pre><code class=\"ruby\">class TelegramAuth   def initialize(app)     @app = app   end    def call(env)     # \u0417\u0434\u0435\u0441\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f redis \u043c\u044b \u043d\u0430\u0445\u043e\u0434\u0438\u043c \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u043c      # \u043f\u043e \u0442\u043e\u043a\u0435\u043d\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0435\u0433\u043e user_id \u0432 \u0441\u0435\u0441\u0441\u0438\u044e      request = Rack::Request.new(env)      token = request.params['tg_token']      if token.present?       user_info_json = REDIS.get(token)       user_info = JSON.parse(user_info_json)       user = User.find_or_initialize_by(telegram_id: user_info['telegram_id'])       user.update(         name: user_info['name'],         username: user_info['username']       )       env['rack.session'][:user_id] = user.id       REDIS.del(token)       return [302, {'Location' => '\/'}, []]     else       puts \"Some error\"     end          @app.call(env)   end end <\/code><\/pre>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0434\u043b\u044f middleware \u0432 <code>config\/application.rb<\/code>, \u0432\u043e\u0442 \u043a\u0430\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c <code>application.rb<\/code> :<\/p>\n<pre><code class=\"ruby\">require_relative \"boot\" require_relative '..\/app\/middleware\/telegram_auth' require \"rails\/all\"  # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups)  module InvestmentGame<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-426520","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/426520","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=426520"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/426520\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=426520"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=426520"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=426520"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}