Creating telegram web apps and interacting with them in telegram bots
In the Bot API 6.0 update, Telegram bots got a lot of new features. The most notable one for developers is Telegram web apps (web apps inside Telegram). With this innovation, developers can connect web applications to their bots, which open in an additional window, which greatly expands the toolkit and, consequently, the functionality of Telegram bots.
Telegram apps are known for their speed, smoothness, and cross-platform design. Your web app should ideally follow these principles.
All elements should be responsive and designed with mobile device orientation in mind.
Interactive elements should mimic the style, behavior, and purpose of existing UI components.
All animations should be smooth, ideally 60 frames per second.
All input and images should include a label for accessibility.
The application should provide a seamless experience by tracking changes in theme colors using the API, and using them appropriately.
Let's put it into practice!
Button creation
The first thing we need to do is to create a button to launch the web application. This can be done in two ways:
1. Using @botfather - the button on the bottom left
1.1 Go to @botfather and write the command: /setmenubutton
1.2 Next, select the bot that needs a web application button
1.3 Send the link where our web application is available
1.4 Write the name of the button - it will be displayed at the bottom left.
That's it. In general, if you have a responsive site and wanted to just add it to the bot, you can stop here.
Only when adding a button in this way can we get the user information.
2. In the bot code - keyboard buttons.
Launching the bot from an inline button gives the same results as the previous option. However, launching from keyboard button allows you to send data from the web application to the bot.
I'll show you pytelegrambotapi as an example - I realize that it's not the most popular library for writing telegram bots, but it happens to be the one I use. If you're using another library/language, I think you'll have no trouble following the analogy. You can see the example at once or go to the repository and see the code with comments:
2.1 Do all the standard things to start the bot - library import, token input, infinity_polling, start command handler. If you don't know what I mean, this is the place to start.
2.2 Create a function that will return a keyboard with the desired button.
In order to create a button, we need to first create a WebAppInfo object with a url to our website inside it.
def webAppKeyboard(): #create keyboard
keyboard = types.ReplyKeyboardMarkup(row_width=1) #создаем клавиатуру
webAppTest = types.WebAppInfo("https://telegram.mihailgok.ru") #webappinfo - url storage format
one_butt = types.KeyboardButton(text="Test Page", web_app=webAppTest) #create webapp button
keyboard.add(one_butt) #add button in keyboard
return keyboard #return the key
2.3 Send a message with our keyboard when sending a command or any other action:
bot.send_message( message.chat.id, 'Hi, I'm a bot for checking telegram webapps!)', reply_markup=webAppKeyboard())
It's done. We have the buttons.
This is where you can generally end up if you just wanted a user to be able to open your site from a bot (for something). The way it would work is like this:
Working with the web application
Now we go to our web application. At the time of writing, any link opens, even to codepen.
Initialization
To interact with telegram we plug in a script:
<script src="https://telegram.org/js/telegram-web-app.js"></script>
After that we will have an object available to us: window.Telegram.WebApp
Write it into a variable and start our work.
let tg = window.Telegram.WebApp;
What can we do now? Not as much as we would like, but not a lot. The application consists of: the main button (telegram object) and the page itself, which is loaded by the link. Other elements of the telegram interface are not available to us. However, the user's theme colors are available:
Colors
These are available in hex format as css variables:
var(--tg-theme-bg-color) var(--tg-theme-text-color) var(--tg-theme-hint-color) var(--tg-theme-link-color) var(--tg-theme-button-color) var(--tg-theme-button-text-color)
Or as a ThemeParams object in js (instead of window.Telegram.WebApp I use the tg variable):
tg.ThemeParams.bg_color tg.ThemeParams.text_color tg.ThemeParams.hint_color tg.ThemeParams.link_color tg.ThemeParams.button_color tg.ThemeParams.button_text_colorString
But be careful, colors are an optional parameter, so it's worth checking if they are there before you use them.
There is also an event handler for changing the color scheme:
Telegram.WebApp.onEvent(themeChanged, function(){});
If you change the color scheme or window size, you can change something in our web application as well.
Basic features
Colors have been dealt with - now let's get to the other main parameters:
tg.initData //получаем данные от пользователя в виде строки (работает только при запуске из меню команд бота).
tg.initDataUnsafe // получаем данные от пользователя в виде объекта (работает только при запуске из меню команд бота).
tg.isExpanded // возвращает true, если приложение открыто на всю высоту, false - если нет.
tg.viewportHeight // вернёт ширину окна.
tg.sendData(data) // отправляет данные боту в строковом виде, после чего закрывает приложение (работает только если приложение запущено с keyboard button).
tg.ready() // метод позволяет отследить, когда приложение готово к отображению.
tg.expand() // метод позволяет растянуть окно на всю высоту.
tg.close() // метод закрывает приложение.
Main button
UPD: Now, with the Bot API 7.10 update, this button is called BottomButton
, and an additional SecondaryButton
has also been added.
We can interact with the button at the bottom of the application. Change its text, background and text color, show/hide it, make it active and deactivate it:
tg.MainButton.text // button text, default: “Continue”
tg.MainButton.color // text color
tg.MainButton.textColor // background color
tg.MainButton.isVisible // whether the button is visible (false by default)
tg.MainButton.isActive // whether the button is active (default is true)
tg.MainButton.setText(text) // method to set text
tg.MainButton.onClick(callback) // method when the button is clicked
tg.MainButton.show() // show button
tg.MainButton.hide() // hide button
tg.MainButton.enable() // make active
tg.MainButton.disable() // make inactive
tg.MainButton.setParams(params) // set parameters as an object
Web App User
And more user information, we can parse the tg.initData
string or use the tg.initDataUnsafe
object:
tg.initDataUnsafe.user.id // unique user identifier
tg.initDataUnsafe.user.isBot // whether the user is a bot (true/false)
tg.initDataUnsafe.user.first_name // user name
tg.initDataUnsafe.user.last_name // user's “last name”
tg.initDataUnsafe.user.username // user username
tg.initDataUnsafe.user.language_code // user's language code
Пишем веб-приложение (web app)
With this information, we can write a small application that will visualize the main features.
Don't forget to connect the script:
<script src="https://telegram.org/js/telegram-web-app.js"></script>
1. Let's create a small html base of our web app:
<body>
<div id="usercard"> <!--Карта профиля, человека, который к нам обратился-->
</div>
<p>Just text</p> <!--Просто текст для проверки-->
<a class="link" href="https://mihailgok.ru">Link</a> <!--Просто ссылка для проверки-->
<p class="hint">Some little hint</p> <!--Просто текст-подсказка для проверки-->
<button id="btn" class="button">Show/Hide Main Button</button> <!--Кнопка, чтобы скрыть / показать основную кнопку-->
<button id="btnED" class="button">Enable/Disable Main Button</button> <!--Кнопка, чтобы сделать кнопку активной/неактивной-->
</body>
Just text
Some little hint
Show/Hide Main ButtonEnable/Disable Main Button
2. Let's write changes to the text of the main button and change the color:
let tg = window.Telegram.WebApp; //get telegram webapp object
tg.expand(); //expand to the whole window
tg.MainButton.text = “Changed Text”; //change button text
tg.MainButton.setText(“Changed Text1”); //change the button text otherwise
tg.MainButton.textColor = “#F55353”; //change button text color
tg.MainButton.color = “#143F6B”; //change button background color
tg.MainButton.setParams({“color”: “#143F6B”}); //change all parameters in this way
3. Next, let's put an event handler on the first html button and show/hide the main telegram button when clicked:
btn.addEventListener('click', function(){ //highlight the html button click event
if (tg.MainButton.isVisible){ //if the button is shown
tg.MainButton.hide() //hide the button
}
else{ //otherwise
tg.MainButton.show() //show
}
});
4. Another event handler for the second html-button, which will activate/deactivate the main telegram-button when clicked:
let btnED = document.getElementById(“btnED”); //get activate/deactivate button
btnED.addEventListener('click', function(){ //hang the html button click event
if (tg.MainButton.isActive){ //if the button is shown
tg.MainButton.setParams({“color”: “#E0FFFFFF”}); //change color
tg.MainButton.disable() //hide the button
}
else{ //otherwise
tg.MainButton.setParams({“color”: “#143F6B”}); //change color
tg.MainButton.enable() //display
}
});
5. Finally send the data when you click on the main telegram button:
Telegram.WebApp.onEvent('mainButtonClicked', function(){
tg.sendData(“some string that we need to send”);
//when the main button is clicked, send data in string form
});
Through this method we can retrieve the data from the web application in the bot.
6. We will also display all information about the user
(will only be available when launched from the button added with @botfather).
We can access: id / isBot / first_name / last_name / username / language_code
let usercard = document.getElementById('usercard'); // get usercard block
let profName = document.createElement('p'); //create paragraph
profName.innerText = `${tg.initDataUnsafe.user.first_name}
${tg.initDataUnsafe.user.last_name}
${tg.initDataUnsafe.user.username} (${tg.initDataUnsafe.user.language_code})`;
// output first name, “last name”, dash username and language code
usercard.appendChild(profName); //add
let userid = document.createElement('p'); //create another paragraph
userid.innerText = `${tg.initDataUnsafe.user.id}`; //show user_id
usercard.appendChild(userid); //add
7. And add styles using telegram-css variables:
body{
color: var(--tg-theme-text-color);
background: var(--tg-theme-bg-color);
display: flex;
flex-direction: column;
align-items: center;
font-size: 18px;
}
.hint{
color: var(--tg-theme-hint-color);
}
.link{
color: var(--tg-theme-link-color);
}
.button{
background: var(--tg-theme-button-color);
color: var(--tg-theme-button-text-color);
border: none;
font-size: 18px;
}
.button:not(:last-child){
margin-bottom: 20px
}
#usercard{
text-align: center;
}
The result is a web application like this:
It displays the basic features - getting information about the user, engaging their theme colors and controlling the app's main button.
Getting data from the web app in the bot
Now we hang an event handler on the message that the web app sends in the send method (it will work only with the keyboard button):
@bot.message_handler(content_types=“web_app_data”) #receive sent data
def answer(webAppMes):
print(webAppMes) #all the information about the message
print(webAppMes.web_app_data.data) #exactly what we sent to the bot
bot.send_message(webAppMes.chat.id, f “received info from web application: {webAppMes.web_app_data.data}")
#send a message in response to sending data from the web application
And that's it - now we can get information from the site and respond to it.
There are other ways - but that's another story.
Source: github, codepen
Sample bot
______________
Subscribe to my newsletter
Read articles from Mihail Gok directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Mihail Gok
Mihail Gok
Frontend Developer, Publisher, Coder