Создание HTML формы.
Инструкция для разработчика веб-формы.
Актуальный исходный код веб-формы реализующий все три вида форм смотрите по ссылке: go-to-the-mars.html
Пример работы веб-формы приведенной выше смотрите в Telegram боте https://t.me/metabot_test_form_bot
Исходный код примера веб-формы
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Заявка для полета на Марс</title>
<!-- Подключаем Telegram Web App -->
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<!-- Подключаем JQuery и JQ suggestions для dadata -->
<!-- PS: JQuery подключать не обязательно, у вас могут быть свои библиотеки для работы с формой и отпарвки ajax запросов -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://app.metabot24.com/lib/jquery.suggestions/js/jquery.suggestions.min.js"></script>
<link href="https://app.metabot24.com/lib/jquery.suggestions/css/suggestions.min.css" rel="stylesheet"/>
<script language="JavaScript">
let botId = ID_ВАШЕГО_БОТА
let botToken = 'ТОКЕН_ВАШЕГО_БОТА'
let dadataToken = 'ТОКЕН_DADATA'
// URL для POST запроса на который будет отправлять данные с формы
// В качестве примера мы отправляем напрямую в бота
// Но в вашем production боте вы должны отправлять на данные на бэк
// А с бэка уже пересылать в бота, чтобы, например "не светить" токен бота в коде html-формы
let sendUrl = '/api/v1/bots/' + botId + '/call/submit-form'
let sendBody = {}
let sendHeaders = {}
let mode = null
let tgWebApp = null
$(function () {
/* Определяем режим работы формы
Режим передается в query url (GET параметр mode=)
Режимы текущей html:
- '' - пустая стркоа или null, когда в чат-боте ссылка на форму приходит в виде ссылки
это универсальный вариант который будет работать в любом мессенджере
- 'tg_inline' - для Telegram чат-бота, когда ссылка на форму приходит в виде inline-кнопки
- 'tg_keyboard' - для Telegram чат-бота, когда ссылка на форму приходит в виде keyboard-кнопки
Для вашего бота может быть достаточно одного из режимов
В качестве примера просто приведены 3 варианта, чтобы вы могли выбрать подходящий и понять разницу
*/
if (typeof (urlParams["mode"]) === 'string') {
mode = urlParams["mode"]
}
// Переменная для доступа к Telegram Web App, чтобы не писать везде window.Telegram.WebApp
if (mode === 'tg_inline' || mode === 'tg_keyboard') {
if (window.Telegram && window.Telegram.WebApp) {
tgWebApp = window.Telegram.WebApp
}
}
// Инициалиця Dadata для автокомплита поля с адресом
$(".dadata-suggestion").suggestions({
token: dadataToken,
type: "ADDRESS"
})
// Получаем хэш-код лида из request url (GET параметр q=)
$('#q').val(urlParams["q"])
// Событие нажатия на кнопку "Отправить данные"
$(document).on('click', '#submit-mars-form', (e) => {
let form = $('#form-mars')
if (!form[0].checkValidity()) {
form[0].reportValidity()
return
}
let formData = getFormData(form)
formData['tg_query_id'] = ''
formData['mode'] = mode
if (mode === 'tg_inline') {
if (tgWebApp && tgWebApp.initDataUnsafe && tgWebApp.initDataUnsafe.query_id) {
formData['tg_query_id'] = tgWebApp.initDataUnsafe.query_id
}
}
if (tgWebApp) {
// https://core.telegram.org/bots/webapps#initializing-web-apps
tgWebApp.expand() // необязательно
tgWebApp.ready() // необязательно
}
// Для универсального режима или режима tg_inline
if (mode !== 'tg_keyboard') {
// Отправляем данные внутри script_request_params и указываем токен и др заголовки для Metabot API
// Но в вашем production, здесь вы должны просто отправить данные на ваш бэк, а с бэка уже в Metabot API
// отправлять необзяталеьно через JSON API, можно делать это просто через ACTION формы SUBMIT кнопку на форме
//
// Если вы реализуете универсальный режим и выполняете отправку через action forma(сабмит без REST API),
// то ваша форма разрывается на два шага
// - заполнение формы клиентом
// - получаем данные на бэке
// и после приема данных рендерим опять HTML в коде которого размещаем JavaScript который закроет страницу (вызовет метод closeForm())
// Поэтому проще данные на бэк отправить по REST API и сразу же закрыть форму
// Именно такой вариант и реализован в данной HTML форме
sendBody = {"script_request_params": formData} //form.serializeArray()
sendHeaders = {
"Authorization": "Bearer " + botToken,
'Content-Type': 'application/json',
'Accept': 'application/json',
}
// Отправка запроса с помощью библиотеки JQuery
sendJQueryRequest('POST', sendUrl, sendBody, sendHeaders, function (response, isError, jqXHR, textStatus, errorThrown) {
if (!isError) {
// Запрос завершён. Здесь можно обрабатывать результат.
//console.log(response)
closeForm()
} else {
// Произошла ошибка
alert("Ошибка обработки API запроса")
console.log(jqXHR)
}
})
// Отправка запроса без дополнительных библиотек (с помощью XHR)
// Могут быть проблемы с отправкой, возможно нужна корректировка кода под ваш бэкенд вашего сайта
/*sendXhrRequest('POST', sendUrl, sendBody, sendHeaders, function(xhr) {
//https://developer.mozilla.org/ru/docs/Web/API/XMLHttpRequest/send#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80_get
if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
// Запрос завершён. Здесь можно обрабатывать результат.
console.log(xhr)
closeForm()
} else {
// Произошла ошибка
alert("Ошибка обработки API запроса")
console.log(xhr)
}
})*/
} else {
// Если это режим tg_keyboard
// Внимание! Лимит строки для sendData - 4096 байт !
// Поэтому такой режим менее универсален, хотя на первый взгляд проще и не требует бэкенда
// Но в будущем могут возникнуть проблемы, если форма будет усложнена
// Если необходимо вывести сообщение (например для отладки),
// тк обычные alert и console.log для telegram Web App не сработают
//tgWebApp.showAlert('Все ок!')
tgWebApp.sendData(JSON.stringify(formData));
// Если не выполняем sendData, то закрываем форму сами
//closeForm()
}
})
})
/**
* Метод для закрытия формы
*/
function closeForm() {
if (mode === null || mode === '') {
location.href = "tg://resolve?domain=metabot_test_form_bot"
window.close()
} else {
tgWebApp.close()
}
}
// https://stackoverflow.com/questions/11338774/serialize-form-data-to-json
function getFormData($form) {
var unindexed_array = $form.serializeArray();
var indexed_array = {};
$.map(unindexed_array, function (n, i) {
indexed_array[n['name']] = n['value'];
});
return indexed_array;
}
//https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
var urlParams = (function (a) {
if (a == "") return {}
var b = {}
for (var i = 0; i < a.length; ++i) {
var p = a[i].split('=', 2)
if (p.length == 1)
b[p[0]] = ""
else
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "))
}
return b
})(window.location.search.substr(1).split('&'))
// Отправка POST запроса по API с помощью JQuery
// https://reqbin.com/code/javascript/wzp2hxwh/javascript-post-request-example
function sendJQueryRequest(method, url, data, jsonHeaders, callback) {
$.ajax({
url: url,
type: method,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
async: false,
headers: jsonHeaders,
data: JSON.stringify(data),
success: function (response) {
callback(response, false)
},
error: function (jqXHR, textStatus, errorThrown) {
//alert("Произошла ошибка при обработке API запроса")
callback(null, true, jqXHR, textStatus, errorThrown)
}
})
}
// Отправка POST запроса по API с помощью XHR
// https://reqbin.com/code/javascript/wzp2hxwh/javascript-post-request-example
function sendXhrRequest(method, url, data, jsonHeaders, callback) {
let xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader("Accept", "application/json")
xhr.setRequestHeader("Content-Type", "application/json")
if (typeof (jsonHeaders) != "undefined") {
for (let key in jsonHeaders) {
xhr.setRequestHeader(key, jsonHeaders[key])
}
}
xhr.onload = () => console.log(xhr.responseText)
xhr.onload = function () {
// Запрос завершён. Здесь можно обрабатывать результат.
callback(xhr)
};
//Вызывает функцию при смене состояния.
//xhr.onreadystatechange = function() {
// callback(xhr)
//}
xhr.send(JSON.stringify(data))
}
</script>
<!-- Стили формы, в вашей релизации будет свой блок кода, подлючаемый в виде css файла -->
<style>
body {
background-color: #070619;
/*width: 100%;
height: 100%;*/
font-size: 18px;
}
.main-container {
width: 95%;
height: 95%;
}
form {
color: #fff;
border: 1px solid silver;
border-radius: 10px;
padding: 10px;
margin: 10px auto 0 auto;
width: 95%;
max-width: 500px;
height: 100%;
}
form .form-title {
font-size: 20px;
text-align: center;
font-weight: bold;
}
form .form-group {
margin: 10px;
}
form .form-group input {
font-size: 18px;
}
form .form-group input[type=checkbox] {
width: 20px;
height: 20px;
}
form .form-group input.dadata-suggestion {
max-width: 75%;
}
form .form-group select {
font-size: 18px;
}
.suggestions-suggestions {
color: #000
}
form .form-button {
text-align: center;
margin-bottom: 10px;
}
form .send-button {
font-size: 18px;
border: 1px solid #fff;
border-radius: 3px;
background-color: #fff;
padding: 5px;
font-weight: bold;
text-decoration: none;
}
form .form-button button {
font-size: 18px;
font-weight: bold;
}
.mars {
position: absolute;
left: calc(55vw);
/*right: 0;*/
top: 0;
z-index: -10;
opacity: 0.8;
}
</style>
</head>
<body>
<!-- HTML КОД формы -->
<div class="main-container">
<!--Для отправки с помощью submit укажите аттрибут action данной формы-->
<form id="form-mars" autocomplete="off">
<div class="form-title">Заявка для полета на Марс</div>
<input type="hidden" id="q" name="q" value="">
<div class="form-group">
<label for="name">
Ваше имя:
</label>
<div>
<input type="text" name="name" id="name" placeholder="Юрий Гагарин" required autofocus>
</div>
</div>
<div class="form-group">
<label for="email">
Почта:
</label>
<div>
<input type="email" name="email" id="email" placeholder="yuri@gagarin.ru">
</div>
</div>
<div class="form-group">
<label for="age">
Возраст:
</label>
<div>
<input type="number" name="age" id="age" min=12 max=777 step=1>
</div>
</div>
<div class="form-group">
<label for="specialization">
Профессия:
</label>
<div>
<select name="specialization" id="specialization" required>
<option value="engineer" selected>Инженер</option>
<option value="scientist">Учёный</option>
<option value="psychologist">Психолог</option>
<option value="other">Другая</option>
</select>
</div>
</div>
<div class="form-group">
<label for="address">
Ваш адрес проживания:
</label>
<input class="dadata-suggestion" type="text" name="address" id="address" placeholder="" required>
</div>
<div class="form-group">
<label for="is_qualified">
Прошел курсы в Центре<br>подготовки космонавтов
<input type="checkbox" name="is_qualified" id="is_qualified" value="1">
</label>
</div>
<div class="form-group">
<label for="has_experience">
Я уже летал в космос (имею опыт)
<input type="checkbox" name="has_experience" id="has_experience" value="1">
</label>
</div>
<!-- Если нужна отправка фото -->
<!--<div class="form-group">
<label>
Фото:
<input type="file" accept="image/jpeg" name="photo" required>
</label>
</div>-->
<div class="form-button">
<!--<button type="submit">Отправить заявку</button>--> <!-- Для отправки с помощью submit формы -->
<a class="send-button" id="submit-mars-form" href="#">Отправить заявку</a>
</div>
</form>
<img class="mars" src="./mars1.gif"/>
</div>
</body>
</html>
При использовании данного примера замените в следующих строках данные на актуальные для вашего бота:
- let botId = ID_ВАШЕГО_БОТА;
- let botToken = 'ТОКЕН_ВАШЕГО_БОТА';
- let dadataToken = 'ТОКЕН_DADATA'.
И измените код отправки заполненных данных, чтобы данные сначала отправлялись на ваш бэк, а затем с бэка в Metabot API. Также уберите отправку данных внутри параметра script_request_params, чтобы данные к вам на бэк отправлялись без этого параметра, но когда отправляете в Metabot API учитывайте эту особенность.
Речь идет про строку:
sendBody = {"script_request_params": formData}
Которую для вас нужно заменить на:
sendBody = formData
Также для понимания принципа работы изучите комментарии в HTML-коде.
Код не требует большого уровня владения HTML или JS для создания подобной формы, но заметим, что важной частью является UI/UX и может потребоваться профессиональный Frontend-разработчик, также необходимо учесть отработку всех исключительных ситуаций, например если произошел сбой API, о чем было упомянуто в теоретической части, во введении, заметим что выше приведен просто пример, все аспекты не проработаны досконально, тк это возможно только на конкретном рабочем примере для вашего бота.
Нет комментариев