Создание 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, о чем было упомянуто в теоретической части, во введении, заметим что выше приведен просто пример, все аспекты не проработаны досконально, тк это возможно только на конкретном рабочем примере для вашего бота.
Нет комментариев для отображения
Нет комментариев для отображения