Creating telegram web app and interacting with them

Tags: телеграм, боты, bot api

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>
<body> <div id=“usercard”> <!–Profile map, of the person who contacted us–> </div> <p>Just text</p> <!–Just text to check–> <a class=“link” href=“https://mihailgok.ru”>Link</a> <!–Just a link to check–> <p class=“hint”>Some little hint</p> <!–Just a text hint to check–> <button id=“btn” class=“button”>Show/Hide Main Button</button> <!–Button to hide/show the main button–> <button id=“btnED” class=“button”>Enable/Disable Main Button</button> <!–Button to make the button active/inactive–> </body>

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: githubcodepen
Sample bot

14.09.2024
Михаил Гок