├── .env ├── LICENSE ├── README.md ├── bot.py ├── requirements.txt ├── server.py ├── static ├── captcha │ ├── captcha-bg.png │ ├── script.js │ └── style.css ├── captchaV2 │ └── styles.css ├── form-template │ └── style.css ├── qrCode │ ├── script.js │ └── styles.css └── telegram.svg └── templates ├── captcha.html ├── captchav2.html ├── demoForm.html └── qrCode.html /.env: -------------------------------------------------------------------------------- 1 | API_TOKEN = '1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ' # Token from @BotFather 2 | SITE_KEY = "re-CAPTCHA-site-key" # Site key from https://www.google.com/recaptcha/admin 3 | SECRET_KEY = "re-CAPTCHA-secret-key" # Secret key from https://www.google.com/recaptcha/admin 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Soham Datta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Web Apps ➜ ***[@pytba_web_app_bot](https://t.me/pytba_web_app_bot/)*** 2 | 3 | This is a collection of some Web Apps that I designed for Telegram. 4 | 5 | All the Web Apps are designed using **[pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI)**. 6 | 7 | ## List of Web Apps included here: 8 | 9 | - **Demo Forms** : An easy way to collect inputs from users supporting a lot of input types (dates, files, etc.) 10 | 11 | - **CAPTCHA** : An simple verification system to keep spammers away! 12 | 13 | - **re-CAPTCHA** : Google's re-CAPTCHA system integrated within Telegram to filter unwanted spammers. This can be implemented within large groups. 14 | 15 | - **QR-Code** : A built in QR Code scanner within Telegram. 16 | 17 | The main purpose for this project is to help new-comers to start designing their own Mini Apps! 18 | 19 | **If you like this project, kindly give a star! ⭐** 20 | 21 | **Contributions are welcome! 😇** 22 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import os 2 | import dotenv 3 | dotenv.load_dotenv() 4 | 5 | import telebot 6 | from telebot.types import WebAppInfo 7 | from telebot.types import InlineKeyboardMarkup 8 | from telebot.types import InlineKeyboardButton 9 | 10 | bot = telebot.TeleBot(os.getenv('API_TOKEN'), parse_mode="HTML") 11 | 12 | web_apps = [ 13 | {"label" : "Demo Forms", "link" : "https://your-server.domain/demoForm"}, 14 | {"label" : "CAPTCHA", "link" : "https://your-server.domain/captcha"}, 15 | {"label" : "QR Code", "link" : "https://your-server.domain/qrCode"}, 16 | {"label" : "re-CAPTCHA", "link" : "https://your-server.domain/captchav2"}, 17 | ] 18 | 19 | @bot.message_handler(commands=["start"]) 20 | def start(message): 21 | 22 | markup = InlineKeyboardMarkup() 23 | markup.row(InlineKeyboardButton(web_apps[0]["label"], 24 | web_app=WebAppInfo(web_apps[0]["link"]))) 25 | markup.row( 26 | InlineKeyboardButton("⬅️", callback_data="web-app:0"), 27 | InlineKeyboardButton("➡️", callback_data="web-app:2") 28 | ) 29 | 30 | bot.send_message(message.chat.id, "Hey there! " 31 | "Wanna see some cool Telegram Web Apps? 🔥\n\n" 32 | "Browse using the butons below 👇🏻", reply_markup=markup) 33 | 34 | 35 | @bot.callback_query_handler(func=lambda call: True) 36 | def callback_listener(call): 37 | 38 | _id, data = call.id, call.data 39 | 40 | if data[:7] == "web-app": 41 | index = int(data[8:]) 42 | 43 | if index == 0: 44 | bot.answer_callback_query(_id, "Oops! Start of list!", show_alert=True) 45 | elif index > len(web_apps): 46 | bot.answer_callback_query(_id, "Oops! End of list!", show_alert=True) 47 | else: 48 | prev_button = InlineKeyboardButton("⬅️", callback_data=f"web-app:{index-1}") 49 | next_button = InlineKeyboardButton("➡️", callback_data=f"web-app:{index+1}") 50 | web_app_btn = InlineKeyboardButton(web_apps[index-1]["label"], 51 | web_app=WebAppInfo(web_apps[index-1]["link"])) 52 | 53 | markup = InlineKeyboardMarkup() 54 | markup.row(web_app_btn) 55 | markup.row(prev_button, next_button) 56 | 57 | bot.edit_message_reply_markup( 58 | call.message.chat.id, 59 | call.message.id, reply_markup=markup) 60 | 61 | elif data[:7] == "confirm": 62 | bot.send_message(int(data[8:]), "Thanks for trying out the WebApp.\n\ 63 | \nIn case you liked it, kindly star ⭐ the project on\ 64 | GitHub.\n\ 65 | \nContributions are welcome! 😇") 66 | bot.edit_message_reply_markup(inline_message_id=call.inline_message_id) 67 | 68 | @bot.message_handler(commands=["help"]) 69 | def help(message): 70 | bot.send_message(message.chat.id, 71 | "To report a bug, please visit the\ 72 | issues page on\ 73 | GitHub.\n\ 74 | \nFor any other questions, contact developer ➜ @tech_savvy_guy") 75 | 76 | if __name__ == "__main__": 77 | print(f'@{bot.get_me().username} is up and running! 🚀') 78 | bot.infinity_polling(skip_pending=True) 79 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | requests 3 | python-dotenv 4 | pyTelegramBotAPI 5 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import dotenv 3 | import requests 4 | dotenv.load_dotenv() 5 | 6 | import flask 7 | from flask import request 8 | from flask import redirect 9 | from flask import render_template 10 | 11 | import telebot 12 | from telebot.types import InlineKeyboardMarkup 13 | from telebot.types import InlineKeyboardButton 14 | from telebot.types import InputTextMessageContent 15 | from telebot.types import InlineQueryResultArticle 16 | 17 | from telebot.util import parse_web_app_data 18 | from telebot.util import validate_web_app_data 19 | 20 | app = flask.Flask(__name__, static_url_path="/static") 21 | bot = telebot.TeleBot(os.getenv("API_TOKEN"), parse_mode="HTML") 22 | 23 | @app.route('/') 24 | def index(): 25 | return "A collection of Telegram Mini Apps" 26 | 27 | # ------------------- Demo Form ------------------- # 28 | 29 | @app.route('/demoForm') 30 | def demoForm(): 31 | return flask.render_template("demoForm.html") 32 | 33 | @app.route('/demoFormResponse', methods=["POST"]) 34 | def demoFormResponse(): 35 | raw_data = request.json 36 | name = raw_data["name"] 37 | date = raw_data["date"] 38 | email = raw_data["email"] 39 | country = raw_data["country"] 40 | initData = raw_data["initData"] 41 | 42 | isValid = validate_web_app_data(bot.token, initData) 43 | 44 | if isValid: 45 | web_app_data = parse_web_app_data(bot.token, initData) 46 | query_id = web_app_data["query_id"] 47 | bot.answer_web_app_query(query_id, InlineQueryResultArticle( 48 | id=query_id, title="VERIFICATION FAILED!", 49 | input_message_content=InputTextMessageContent( 50 | f"Demo Form:\n\nName: {name}\n\nBorn: {date}\n\ 51 | \nEmail: {email}\n\nCountry: {country}", parse_mode="HTML"), 52 | reply_markup=InlineKeyboardMarkup().row(InlineKeyboardButton( 53 | "CLICK TO CONTINUE ✅", callback_data=f"confirm-{web_app_data['user']['id']}")))) 54 | 55 | return redirect("/") 56 | 57 | # ------------------- Demo Form ------------------- # 58 | 59 | 60 | # ------------------- Text Captcha ------------------- # 61 | 62 | @app.route('/captcha') 63 | def captcha(): 64 | return flask.render_template("captcha.html") 65 | 66 | @app.route('/captchaResponse', methods=['POST']) 67 | def captchaResponse(): 68 | raw_data = flask.request.json 69 | isbot = raw_data["isbot"] 70 | initData = raw_data["initData"] 71 | attempts = raw_data["attempts"] 72 | 73 | isValid = validate_web_app_data(bot.token, initData) 74 | 75 | if isValid: 76 | if not isbot: 77 | web_app_data = parse_web_app_data(bot.token, initData) 78 | query_id = web_app_data["query_id"] 79 | bot.answer_web_app_query(query_id, InlineQueryResultArticle( 80 | id=query_id, title="VERIFICATION PASSED!", 81 | input_message_content=InputTextMessageContent( 82 | "Captcha verification passed ✅\n\ 83 | \nIt seems that you're indeed a human! 😉", 84 | parse_mode="HTML"), reply_markup=InlineKeyboardMarkup().row( 85 | InlineKeyboardButton("CLICK TO CONTINUE ✅", 86 | callback_data=f"confirm-{web_app_data['user']['id']}")))) 87 | else: 88 | if attempts == 3: 89 | web_app_data = parse_web_app_data(bot.token, initData) 90 | query_id = web_app_data["query_id"] 91 | bot.answer_web_app_query(query_id, InlineQueryResultArticle( 92 | id=query_id, title="VERIFICATION FAILED!", 93 | input_message_content=InputTextMessageContent( 94 | "Captcha verification failed ❌\n\ 95 | \nI don't trust your human side! 🤔", 96 | parse_mode="HTML"), reply_markup=InlineKeyboardMarkup().row( 97 | InlineKeyboardButton("CLICK TO CONTINUE ✅", 98 | callback_data=f"confirm-{web_app_data['user']['id']}")))) 99 | 100 | return redirect("/") 101 | 102 | # ------------------- Text Captcha ------------------- # 103 | 104 | 105 | # ------------------- QR Code Scanner ------------------- # 106 | 107 | @app.route('/qrCode') 108 | def qrCode(): 109 | return flask.render_template("qrCode.html") 110 | 111 | @app.route('/qrCodeResponse', methods=["POST"]) 112 | def qrCodeResponse(): 113 | raw_data = flask.request.json 114 | initData = raw_data["initData"] 115 | 116 | isValid = validate_web_app_data(bot.token, initData) 117 | 118 | if isValid: 119 | web_app_data = parse_web_app_data(bot.token, initData) 120 | 121 | query_id = web_app_data["query_id"] 122 | 123 | bot.answer_web_app_query(query_id, InlineQueryResultArticle( 124 | id=query_id, title="QR DETECTED!", 125 | input_message_content=InputTextMessageContent( 126 | f"QR Code scanned successfully! 👇🏻\n\ 127 | \n{raw_data['qr']}", parse_mode="HTML"), 128 | reply_markup=InlineKeyboardMarkup().row( 129 | InlineKeyboardButton("CLICK TO CONTINUE ✅", 130 | callback_data=f"confirm-{web_app_data['user']['id']}")))) 131 | 132 | return redirect("/") 133 | 134 | # ------------------- QR Code Scanner ------------------- # 135 | 136 | 137 | # ------------------- Google re-CAPTCHA ------------------- # 138 | 139 | @app.route('/captchav2', methods=["GET", "POST"]) 140 | def captchaV2(): 141 | 142 | if request.method == 'POST': 143 | 144 | recaptcha_response = request.form.get('g-recaptcha-response') 145 | 146 | data = { 147 | 'secret': os.getenv("SECRET_KEY"), 148 | 'response': recaptcha_response 149 | } 150 | response = requests.post( 151 | 'https://www.google.com/recaptcha/api/siteverify', data=data) 152 | result = response.json() 153 | 154 | raw_data = request.form 155 | initData = raw_data["initData"] 156 | 157 | isValid = validate_web_app_data(bot.token, initData) 158 | 159 | if isValid: 160 | 161 | web_app_data = parse_web_app_data(bot.token, initData) 162 | query_id = web_app_data["query_id"] 163 | 164 | if result['success']: 165 | bot.answer_web_app_query(query_id, InlineQueryResultArticle( 166 | id=query_id, title="VERIFICATION PASSED!", 167 | input_message_content=InputTextMessageContent( 168 | "Captcha verification passed ✅\n\ 169 | \nIt seems that you're indeed a human! 😉", 170 | parse_mode="HTML"), reply_markup=InlineKeyboardMarkup().row( 171 | InlineKeyboardButton("CLICK TO CONTINUE ✅", 172 | callback_data=f"confirm-{web_app_data['user']['id']}")))) 173 | else: 174 | 175 | bot.answer_web_app_query(query_id, InlineQueryResultArticle( 176 | id=query_id, title="VERIFICATION FAILED!", 177 | input_message_content=InputTextMessageContent( 178 | "Captcha verification failed ❌\n\ 179 | \nI don't trust your human side! 🤔", 180 | parse_mode="HTML"), reply_markup=InlineKeyboardMarkup().row( 181 | InlineKeyboardButton("CLICK TO CONTINUE ✅", 182 | callback_data=f"confirm-{web_app_data['user']['id']}")))) 183 | 184 | return redirect("/") 185 | 186 | return render_template('captchav2.html') 187 | 188 | # ------------------- Google re-CAPTCHA ------------------- # 189 | 190 | 191 | if __name__ == '__main__': 192 | app.run(host='0.0.0.0') 193 | -------------------------------------------------------------------------------- /static/captcha/captcha-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-savvy-guy/telegram-web-apps/d4054cd4221bad68394c347866e5d78f53a74fbc/static/captcha/captcha-bg.png -------------------------------------------------------------------------------- /static/captcha/script.js: -------------------------------------------------------------------------------- 1 | const captcha = document.querySelector(".captcha"), 2 | reloadBtn = document.querySelector(".reload-btn"), 3 | inputField = document.querySelector(".input-area input"), 4 | checkBtn = document.querySelector(".check-btn"), 5 | statusTxt = document.querySelector(".status-text"), 6 | maxtries = document.querySelector(".error"); 7 | 8 | var attempts = 0; 9 | 10 | Telegram.WebApp.ready() 11 | 12 | let allCharacters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 13 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 14 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 15 | 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 16 | function getCaptcha(){ 17 | for (let i = 0; i < 6; i++) { 18 | let randomCharacter = allCharacters[Math.floor(Math.random() * allCharacters.length)]; 19 | captcha.innerText += ` ${randomCharacter}`; 20 | } 21 | } 22 | getCaptcha(); 23 | reloadBtn.addEventListener("click", ()=>{ 24 | removeContent(); 25 | getCaptcha(); 26 | }); 27 | 28 | checkBtn.addEventListener("click", e =>{ 29 | e.preventDefault(); 30 | statusTxt.style.display = "block"; 31 | let inputVal = inputField.value.split('').join(' '); 32 | if(inputVal == captcha.innerText){ 33 | statusTxt.style.color = "#4db2ec"; 34 | statusTxt.innerText = "Nice! You don't appear to be a robot."; 35 | checkBtn.style.display = "none"; 36 | fetch('/captchaResponse', { 37 | method: 'POST', 38 | headers: { 39 | 'Accept': 'application/json', 40 | 'Content-Type': 'application/json' 41 | }, 42 | body: JSON.stringify({ 43 | isbot: false, attempts: attempts + 1, 44 | initData: window.Telegram.WebApp.initData 45 | }) 46 | }); 47 | attempts = 0; 48 | 49 | }else{ 50 | if (attempts < 2) { 51 | statusTxt.style.color = "#ff0000"; 52 | statusTxt.innerText = "Error! Remaining attempts: " + (2 - attempts); 53 | fetch('/captchaResponse', { 54 | method: 'POST', 55 | headers: { 56 | 'Accept': 'application/json', 57 | 'Content-Type': 'application/json' 58 | }, 59 | body: JSON.stringify({ 60 | isbot: true, attempts: attempts + 1, 61 | initData: window.Telegram.WebApp.initData 62 | }) 63 | }); 64 | inputField.value = ""; 65 | captcha.innerText = ""; 66 | attempts = attempts + 1; 67 | getCaptcha(); 68 | } 69 | 70 | else { 71 | maxtries.style.display = "block"; 72 | statusTxt.style.display = "none"; 73 | maxtries.innerText = "Maximum attempts reached!"; 74 | document.querySelector(".input-area").style.display = "none"; 75 | document.querySelector(".captcha-area").style.display = "none"; 76 | fetch('/captchaResponse', { 77 | method: 'POST', 78 | headers: { 79 | 'Accept': 'application/json', 80 | 'Content-Type': 'application/json' 81 | }, 82 | body: JSON.stringify({ 83 | isbot: true, attempts: attempts + 1, 84 | initData: window.Telegram.WebApp.initData 85 | }) 86 | }); 87 | } 88 | } 89 | }); 90 | 91 | function removeContent(){ 92 | inputField.value = ""; 93 | captcha.innerText = ""; 94 | statusTxt.style.display = "none"; 95 | } -------------------------------------------------------------------------------- /static/captcha/style.css: -------------------------------------------------------------------------------- 1 | /* Import Google font - Poppins & Noto */ 2 | @import url('https://fonts.googleapis.com/css2?family=Noto+Serif:ital@1&family=Poppins:wght@400;500;600&display=swap'); 3 | *{ 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: "Poppins", sans-serif; 8 | } 9 | ::selection{ 10 | color: #fff; 11 | background: #4db2ec; 12 | } 13 | body{ 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | min-height: 100vh; 18 | background: #4db2ec; 19 | } 20 | .wrapper{ 21 | max-width: 485px; 22 | width: 100%; 23 | background: #fff; 24 | padding: 22px 30px 40px; 25 | border-radius: 10px; 26 | box-shadow: 8px 8px 8px rgba(0, 0, 0, 0.05); 27 | } 28 | 29 | .wrapper header{ 30 | color: #4db2ec; 31 | font-size: 33px; 32 | font-weight: 500; 33 | text-align: center; 34 | } 35 | 36 | .wrapper .error{ 37 | display: none; 38 | color: #ff0000; 39 | font-size: 25px; 40 | font-weight: 500; 41 | text-align: center; 42 | } 43 | .wrapper .captcha-area{ 44 | display: flex; 45 | height: 65px; 46 | margin: 30px 0 20px; 47 | align-items: center; 48 | justify-content: space-between; 49 | } 50 | .captcha-area .captcha-img{ 51 | height: 100%; 52 | width: 345px; 53 | user-select: none; 54 | background: #000; 55 | border-radius: 5px; 56 | position: relative; 57 | } 58 | .captcha-img img{ 59 | width: 100%; 60 | height: 100%; 61 | object-fit: cover; 62 | border-radius: 5px; 63 | opacity: 0.95; 64 | } 65 | .captcha-img .captcha{ 66 | position: absolute; 67 | left: 50%; 68 | top: 50%; 69 | width: 100%; 70 | color: #fff; 71 | font-size: 35px; 72 | text-align: center; 73 | letter-spacing: 10px; 74 | transform: translate(-50%, -50%); 75 | text-shadow: 0px 0px 2px #b1b1b1; 76 | font-family: 'Noto Serif', serif; 77 | } 78 | .wrapper button{ 79 | outline: none; 80 | border: none; 81 | color: #fff; 82 | cursor: pointer; 83 | background: #4db2ec; 84 | border-radius: 5px; 85 | transition: all 0.3s ease; 86 | } 87 | .wrapper button:hover{ 88 | background: #2fa5e9; 89 | } 90 | .captcha-area .reload-btn{ 91 | width: 75px; 92 | height: 100%; 93 | font-size: 25px; 94 | } 95 | .captcha-area .reload-btn i{ 96 | transition: transform 0.3s ease; 97 | } 98 | .captcha-area .reload-btn:hover i{ 99 | transform: rotate(15deg); 100 | } 101 | .wrapper .input-area{ 102 | height: 60px; 103 | width: 100%; 104 | position: relative; 105 | } 106 | .input-area input{ 107 | width: 100%; 108 | height: 100%; 109 | outline: none; 110 | padding-left: 20px; 111 | font-size: 20px; 112 | border-radius: 5px; 113 | border: 1px solid #bfbfbf; 114 | } 115 | .input-area input:is(:focus, :valid){ 116 | padding-left: 19px; 117 | border: 2px solid #4db2ec; 118 | } 119 | .input-area input::placeholder{ 120 | color: #bfbfbf; 121 | } 122 | .input-area .check-btn{ 123 | position: absolute; 124 | right: 7px; 125 | top: 50%; 126 | font-size: 17px; 127 | height: 45px; 128 | padding: 0 20px; 129 | opacity: 0; 130 | pointer-events: none; 131 | transform: translateY(-50%); 132 | } 133 | .input-area input:valid + .check-btn{ 134 | opacity: 1; 135 | pointer-events: auto; 136 | } 137 | .wrapper .status-text{ 138 | display: none; 139 | font-size: 18px; 140 | text-align: center; 141 | margin: 20px 0 -5px; 142 | } 143 | 144 | @media (max-width: 506px){ 145 | body{ 146 | padding: 0 10px; 147 | } 148 | .wrapper{ 149 | padding: 22px 25px 35px; 150 | } 151 | .wrapper header{ 152 | font-size: 25px; 153 | } 154 | .wrapper .captcha-area{ 155 | height: 60px; 156 | } 157 | .captcha-area .captcha{ 158 | font-size: 28px; 159 | letter-spacing: 5px; 160 | } 161 | .captcha-area .reload-btn{ 162 | width: 60px; 163 | margin-left: 5px; 164 | font-size: 20px; 165 | } 166 | .wrapper .input-area{ 167 | height: 55px; 168 | } 169 | .input-area .check-btn{ 170 | height: 40px; 171 | } 172 | .wrapper .status-text{ 173 | font-size: 15px; 174 | } 175 | .captcha-area .captcha-img{ 176 | width: 250px; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /static/captchaV2/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Poppins', sans-serif; 7 | max-width: 100%; 8 | max-height: 100%; 9 | } 10 | 11 | body { 12 | height: 100vh; 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | justify-content: center; 17 | background: linear-gradient(135deg, #8c00ff, #00ffd0); 18 | } 19 | 20 | .container { 21 | max-width: 50%; 22 | max-height: 75%; 23 | background-color: #fff; 24 | display: flex; 25 | flex-direction: column; 26 | align-items: center; 27 | justify-content: center; 28 | padding: 50px; 29 | border-radius: 25px; 30 | } 31 | 32 | h1 { 33 | margin-bottom: 30px; 34 | } 35 | 36 | input { 37 | width: 100%; 38 | height: 50px; 39 | border: none; 40 | border-radius: 10px; 41 | padding: 0 15px; 42 | margin-bottom: 20px; 43 | font-size: 16px; 44 | background-color: #00b7ff; 45 | color: #fff; 46 | font-weight: 600; 47 | } 48 | 49 | .g-recaptcha { 50 | margin-bottom: 20px; 51 | } -------------------------------------------------------------------------------- /static/form-template/style.css: -------------------------------------------------------------------------------- 1 | /* ===== Google Font Import - Poformsins ===== */ 2 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); 3 | 4 | *{ 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | font-family: 'Poppins', sans-serif; 9 | } 10 | 11 | body{ 12 | height: 100vh; 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | background-color: #4070f4; 17 | } 18 | 19 | .container{ 20 | position: relative; 21 | max-width: 430px; 22 | width: 100%; 23 | background: #fff; 24 | border-radius: 10px; 25 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); 26 | overflow: hidden; 27 | margin: 0 20px; 28 | } 29 | 30 | .container .forms{ 31 | display: flex; 32 | align-items: center; 33 | height: 440px; 34 | width: 200%; 35 | transition: height 0.2s ease; 36 | } 37 | 38 | 39 | .container .form{ 40 | width: 50%; 41 | padding: 30px; 42 | background-color: #fff; 43 | transition: margin-left 0.18s ease; 44 | } 45 | 46 | 47 | .container .signup{ 48 | opacity: 0; 49 | transition: opacity 0.09s ease; 50 | } 51 | .container.active .signup{ 52 | opacity: 1; 53 | transition: opacity 0.2s ease; 54 | } 55 | 56 | .container.active .forms{ 57 | height: 600px; 58 | } 59 | .container .form .title{ 60 | position: relative; 61 | font-size: 27px; 62 | font-weight: 600; 63 | } 64 | 65 | .form .title::before{ 66 | content: ''; 67 | position: absolute; 68 | left: 0; 69 | bottom: 0; 70 | height: 3px; 71 | width: 30px; 72 | background-color: #4070f4; 73 | border-radius: 25px; 74 | } 75 | 76 | .form .input-field{ 77 | position: relative; 78 | height: 50px; 79 | width: 100%; 80 | margin-top: 30px; 81 | } 82 | 83 | .input-field input{ 84 | position: absolute; 85 | height: 100%; 86 | width: 100%; 87 | padding: 0 35px; 88 | border: none; 89 | outline: none; 90 | font-size: 16px; 91 | border-bottom: 2px solid #ccc; 92 | border-top: 2px solid transparent; 93 | transition: all 0.2s ease; 94 | } 95 | 96 | .input-field input:is(:focus, :valid){ 97 | border-bottom-color: #4070f4; 98 | } 99 | 100 | .input-field i{ 101 | position: absolute; 102 | top: 50%; 103 | transform: translateY(-50%); 104 | color: #999; 105 | font-size: 23px; 106 | transition: all 0.2s ease; 107 | } 108 | 109 | .input-field input:is(:focus, :valid) ~ i{ 110 | color: #4070f4; 111 | } 112 | 113 | .input-field i.icon{ 114 | left: 0; 115 | } 116 | .input-field i.showHidePw{ 117 | right: 0; 118 | cursor: pointer; 119 | padding: 10px; 120 | } 121 | 122 | .form .checkbox-text{ 123 | display: flex; 124 | align-items: center; 125 | justify-content: space-between; 126 | margin-top: 20px; 127 | } 128 | 129 | .checkbox-text .checkbox-content{ 130 | display: flex; 131 | align-items: center; 132 | } 133 | 134 | .checkbox-content input{ 135 | margin: 0 8px -2px 4px; 136 | accent-color: #4070f4; 137 | } 138 | 139 | .form .text{ 140 | color: #333; 141 | font-size: 14px; 142 | } 143 | 144 | .form a.text{ 145 | color: #4070f4; 146 | text-decoration: none; 147 | } 148 | .form a:hover{ 149 | text-decoration: underline; 150 | } 151 | 152 | .form .button{ 153 | margin-top: 35px; 154 | } 155 | 156 | .form .button input{ 157 | border: none; 158 | color: #fff; 159 | font-size: 17px; 160 | font-weight: 500; 161 | letter-spacing: 1px; 162 | border-radius: 6px; 163 | background-color: #4070f4; 164 | cursor: pointer; 165 | transition: all 0.3s ease; 166 | } 167 | 168 | .button input:hover{ 169 | background-color: #265df2; 170 | } 171 | 172 | .form .login-signup{ 173 | margin-top: 30px; 174 | text-align: center; 175 | } -------------------------------------------------------------------------------- /static/qrCode/script.js: -------------------------------------------------------------------------------- 1 | Telegram.WebApp.ready() 2 | Telegram.WebApp.expand() 3 | 4 | function handleQRTextReceived(event) { 5 | var qrText = event.data; 6 | 7 | fetch('/qrCodeResponse', { 8 | method: 'POST', 9 | headers: { 10 | 'Accept': 'application/json', 11 | 'Content-Type': 'application/json' 12 | }, 13 | body: JSON.stringify({ qr: qrText, initData: window.Telegram.WebApp.initData }) 14 | }); 15 | 16 | Telegram.WebApp.offEvent('qrTextReceived', handleQRTextReceived); 17 | 18 | setTimeout(Telegram.WebApp.closeScanQrPopup(), 1000); 19 | } 20 | 21 | Telegram.WebApp.onEvent('qrTextReceived', handleQRTextReceived); -------------------------------------------------------------------------------- /static/qrCode/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Noto+Serif:ital@1&family=Poppins:wght@400;500;625;700;800&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: "Poppins", sans-serif; 8 | } 9 | 10 | ::selection { 11 | color: #fff; 12 | background: #4db2ec; 13 | } 14 | 15 | body { 16 | display: flex; 17 | align-items: center; 18 | justify-content: center; 19 | min-height: 100vh; 20 | background: #4db2ec; 21 | } 22 | 23 | .wrapper { 24 | margin: 20px; 25 | max-width: 400px; 26 | width: 100%; 27 | background: #fff; 28 | padding: 45px 20px 30px 20px; 29 | border-radius: 10px; 30 | box-shadow: 8px 8px 8px rgba(0, 0, 0, 0.05); 31 | display: flex; 32 | flex-direction: column; 33 | align-items: center; 34 | justify-content: center; 35 | } 36 | 37 | .wrapper header { 38 | color: #237db2; 39 | font-size: 35px; 40 | font-weight: 625; 41 | text-align: center; 42 | margin-bottom: 45px; 43 | } 44 | 45 | .wrapper button { 46 | align-items: center; 47 | outline: none; 48 | border: none; 49 | color: #fff; 50 | cursor: pointer; 51 | background: #4db2ec; 52 | border-radius: 5px; 53 | transition: all 0.3s ease; 54 | padding: 10px 20px; 55 | font-weight: 450; 56 | font-size: 25px; 57 | } 58 | 59 | .wrapper button:hover { 60 | background: #2fa5e9; 61 | } -------------------------------------------------------------------------------- /static/telegram.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/captcha.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CAPTCHA 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
TELEGRAM BOT CAPTCHA
15 |
16 |
17 | Captch Background 18 | 19 |
20 | 21 |
22 |
23 | 24 | 25 |
26 |

27 |
28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /templates/captchav2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Google Captcha 10 | 11 | 12 |
13 |

Google reCAPTCHA

14 |
15 |
16 | 17 | 18 |
19 |
20 | 26 | 27 | -------------------------------------------------------------------------------- /templates/demoForm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Form Demo 13 | 34 | 35 | 36 | 37 | 38 |
39 |
40 | 65 |
66 |
67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /templates/qrCode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | QR SCANNER 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
QR SCANNER
16 | 17 |
18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------