Telegram API
From BotFather to 'Hello World'
Introduction
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getMe
Getting Ready
Obtain Your Bot Token
4839574812:AAFD39kkdpWt3ywyRZergyOLMaJhac60qc
Download an IDE
Pick a Framework or Library
Create Your Project
In IntelliJ, go to File > New > Project
.
BotTutorial
├─ .idea
├─ src
│ └─ main
│ └─ java
│ └─ tutorial
│ └─ Main
└─ pom.xml
Add Framework Dependency
<dependencies>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>6.0.1</version>
</dependency>
</dependencies>
Start Coding
Creating a Bot Class
package tutorial;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.objects.Update;
public class BotClass extends TelegramLongPollingBot {
@Override
public String getBotUsername() {
return null;
}
@Override
public String getBotToken() {
return null;
}
@Override
public void onUpdateReceived(Update update) {}
}
Available Methods
Let's look into these 3 methods one by one.
getBotUsername
getBotToken
onUpdateReceived
@Override
public String getBotUsername() {
return "TutorialBot";
}
@Override
public String getBotToken() {
return "4839574812:AAFD39kkdpWt3ywyRZergyOLMaJhac60qc";
}
@Override
public void onUpdateReceived(Update update) {
System.out.println(update);
}
Registering the Bot
public static void main(String[] args) throws TelegramApiException {
TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
botsApi.registerBot(new BotClass());
}
First Run
Receiving Messages
Let's focus on two values for now:
The user - Who sent the message. Access it via
update.getMessage().getFrom()
.The message - What was sent. Access it via
update.getMessage()
.
@Override
public void onUpdateReceived(Update update) {
var msg = update.getMessage();
var user = msg.getFrom();
System.out.println(user.getFirstName() + " wrote " + msg.getText());
}
Sending Messages
To send a private text message, you generally need three things:
The user must have contacted your bot first. (Unless the user sent a join request to a group where your bot is an admin, but that's a more advanced scenario).
You must have previously saved the User ID (
user.getId()
)A
String
object containing the message text, 1-4096 characters.
public void sendText(Long who, String what){
SendMessage sm = SendMessage.builder()
.chatId(who.toString()) //Who are we sending a message to
.text(what).build(); //Message content
try {
execute(sm); //Actually sending the message
} catch (TelegramApiException e) {
throw new RuntimeException(e); //Any error will be printed here
}
}
And proceed to run this in the main
method, right after registering the bot.
For this example, we'll assume your User ID is 1234
.
public static void main(String[] args) throws TelegramApiException {
TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
Bot bot = new Bot(); //We moved this line out of the register method, to access it later
botsApi.registerBot(bot);
bot.sendText(1234L, "Hello World!"); //The L just turns the Integer into a Long
}
If you did everything correctly, your bot should text you Hello World! every time you launch your code. Sending messages to groups or channels – assuming you have the relevant permissions – is as simple as replacing 1234
with the ID of the respective chat.
Try experimenting with other types of messages, like SendPhoto, SendSticker, SendDice… A full list is available starting here.
Echo Bot
Let's practice everything we tried so far by coding an Echo Bot. Its functionality will be rather simple: every text message it receives will be sent right back to the user.
Copying Text
The most intuitive way of coding this is saving the User ID and calling sendText
right after each update.
In other words:
@Override
public void onUpdateReceived(Update update) {
var msg = update.getMessage();
var user = msg.getFrom();
var id = user.getId();
sendText(id, msg.getText());
}
This works for text but can be extended to stickers, media and files.
Copying Everything
There are more specific functions that can be used to copy messages and send them back. Let's build a method to do just that:
public void copyMessage(Long who, Integer msgId){
CopyMessage cm = CopyMessage.builder()
.fromChatId(who.toString()) //We copy from the user
.chatId(who.toString()) //And send it back to him
.messageId(msgId) //Specifying what message
.build();
try {
execute(cm);
} catch (TelegramApiException e) {
throw new RuntimeException(e);
}
}
After replacing the method call inonUpdateReceived
, running the code will result in a fully functional Echo Bot.
This tutorial assumes that updates always contain messages for the sake of simplicity. This may not always be true – be sure to implement all the proper checks in your code to handle every type of update with the appropriate methods.
Executing Commands
To learn what a command is and how it works, we recommend reading this dedicated summary. In this guide, we'll focus on the technical side of things.
Creating Your Command
Begin by opening @BotFather.
Type /mybots
> Your_Bot_Name > Edit Bot > Edit Commands.
Now send a new command, followed by a brief description. For the purpose of this tutorial, we'll implement two simple commands:
scream - Speak, I'll scream right back
whisper - Shhhhhhh
Command Logic
We want the Echo Bot to reply in uppercase when it's in scream mode and normally otherwise.
First, let's create a variable to store the current mode.
public class Bot extends TelegramLongPollingBot {
private boolean screaming = false;
[...]
}
Then, let's change some logic to account for this mode.
public void onUpdateReceived(Update update) {
[...] //Same variables as the previous versions
if(screaming) //If we are screaming
scream(id, update.getMessage()); //Call a custom method
else
copyMessage(id, msg.getMessageId()); //Else proceed normally
}
private void scream(Long id, Message msg) {
if(msg.hasText())
sendText(id, msg.getText().toUpperCase());
else
copyMessage(id, msg.getMessageId()); //We can't really scream a sticker
}
Finally, let's add a couple more lines to the onUpdateReceived
method to process each command before replying.
if(msg.isCommand()){
if(msg.getText().equals("/scream")) //If the command was /scream, we switch gears
screaming = true;
else if (msg.getText().equals("/whisper")) //Otherwise, we return to normal
screaming = false;
return; //We don't want to echo commands, so we exit
}
As you can see, it checks if the message is a command. If it is, the bot enters scream mode. In the update method, we check which mode we are in and either copy the message or convert it to upper case before sending it back.
And that's it. Now the bot can execute commands and change its behavior accordingly.
Naturally, this simplified logic will change the bot's behavior for everyone – not just the person who sent the command. This can be fun for this tutorial but won't work in a production environment – consider using a Map, dictionary or equivalent data structure to assign settings for individual users.
Remember to always implement a few basic global commands. You can practice by implementing a simple feedback to the
/start
command, which we intentionally left out.
Buttons and Keyboards
To streamline and simplify user interaction with your bot, you can replace many text-based exchanges with handy buttons. These buttons can perform a wide variety of actions and can be customized for each user.
Button Types
There are two main types of buttons:
Reply Buttons - used to provide a list of predefined text reply options.
Inline Buttons - used to offer quick navigation, shortcuts, URLs, games and so much more.
Using these buttons is as easy as attaching a ReplyKeyboardMarkup
or an InlineKeyboardMarkup
to your SendMessage
object.
This guide will focus on inline buttons since they only require a few extra lines of code.
Creating Buttons
First of all, let's create some buttons.
var next = InlineKeyboardButton.builder()
.text("Next").callbackData("next")
.build();
var back = InlineKeyboardButton.builder()
.text("Back").callbackData("back")
.build();
var url = InlineKeyboardButton.builder()
.text("Tutorial")
.url("https://core.telegram.org/bots/api")
.build();
Let's go back through the fields we specified:
Text - This is what the user will see, the text that appears on the button
Callback Data - This will be sent back to the code instance as part of a new
Update
, so we can quickly identify what button was clicked.Url - A button that specifies a URL doesn't specify callbackdata since its behavior is predefined – it will open the given link when tapped.
Creating Keyboards
The buttons we created can be assembled into two keyboards, which will then be used to navigate back and forth between two sample menus.
First, add two fields to store the necessary keyboards.
private boolean screaming = false;
private InlineKeyboardMarkup keyboardM1;
private InlineKeyboardMarkup keyboardM2;
Then, build and assign them.
keyboardM1 = InlineKeyboardMarkup.builder()
.keyboardRow(List.of(next)).build();
//Buttons are wrapped in lists since each keyboard is a set of button rows
keyboardM2 = InlineKeyboardMarkup.builder()
.keyboardRow(List.of(back))
.keyboardRow(List.of(url))
.build();
You can place this code wherever you prefer, the important thing is making sure that keyboard variables are accessible from the method call that will send the new menu. If you're confused by this concept and don't know where to put them, just paste them above the command processing flow.
Sending Keyboards
Sending a keyboard only requires specifying a reply markup for the message.
public void sendMenu(Long who, String txt, InlineKeyboardMarkup kb){
SendMessage sm = SendMessage.builder().chatId(who.toString())
.parseMode("HTML").text(txt)
.replyMarkup(kb).build();
try {
execute(sm);
} catch (TelegramApiException e) {
throw new RuntimeException(e);
}
}
You may have noticed that we also added a new parameter,
HTML
. This is called a formatting option and will allow us to use HTML tags and add formatting to the text later on.
Menu Trigger
We could send a new menu for each new user, but for simplicity let's add a new command that will spawn a menu. We can achieve this by adding a new else clause to the previous command flow.
var txt = msg.getText();
if(msg.isCommand()) {
if (txt.equals("/scream"))
screaming = true;
else if (txt.equals("/whisper"))
screaming = false;
else if (txt.equals("/menu"))
sendMenu(id, "<b>Menu 1</b>", keyboardM1);
return;
}
Try sending /menu
to your bot now. If you did everything correctly, you should see a brand new menu pop up.
In a production environment, commands should be handled with an appropriate design pattern that isolates them into different executor classes – modular and separated from the main logic.
Navigation
When building complex bots, navigation is essential. Your users must be able to move seamlessly from one menu to the next.
In this example, we want the Next
button to lead the user to the second menu.
The Back
button will send us back.
To do that, we will start processing incoming CallbackQueries
, which are the results we get after the user taps on a button.
A CallbackQuery
is essentially composed of three main parameters:
queryId - Needed to close the query. You must always close new queries after processing them – if you don't, a loading symbol will keep showing on the user's side on top of each button.
data - This identifies which button was pressed.
from - The user who pressed the button.
Processing in this context just means executing the action uniquely identified by the button, then closing the query.
A very basic button handler could look something like:
private void buttonTap(Long id, String queryId, String data, int msgId) {
EditMessageText newTxt = EditMessageText.builder()
.chatId(id.toString())
.messageId(msgId).text("").build();
EditMessageReplyMarkup newKb = EditMessageReplyMarkup.builder()
.chatId(id.toString()).messageId(msgId).build();
if(data.equals("next")) {
newTxt.setText("MENU 2");
newKb.setReplyMarkup(keyboardM2);
} else if(data.equals("back")) {
newTxt.setText("MENU 1");
newKb.setReplyMarkup(keyboardM1);
}
AnswerCallbackQuery close = AnswerCallbackQuery.builder()
.callbackQueryId(queryId).build();
execute(close);
execute(newTxt);
execute(newKb);
}
With this handler, whenever a button is tapped, your bot will automatically navigate between inline menus. Expanding on this concept allows for endless combinations of navigable submenus, settings and dynamic pages.
Database
Telegram does not host an update database for you – once you process and consume an update, it will no longer be available. This means that features like user lists, message lists, current user inline menu, settings, etc. have to be implemented and maintained by bot developers.
If your bot needs one of these features and you want to get started on data persistence, we recommend that you look into serialization practices and libraries for your language of choice, as well as available databases.
Implementing a database is out of scope for this guide, however, several guides are available online for simple embedded open source software solutions like SQLite, HyperSQL, Derby and many more.
Your language of choice will also influence which databases are available and supported – the list above assumes you followed this Java tutorial.
Hosting
So far, your bot has been running on your local machine – your PC. While this may be good for developing, testingand debugging, it is not ideal for a production environment. You'll want your bot to be available and responsive at all times, but your computer might not always be online.
This can be done in four steps:
Package your code Making your bot easy to move and runnable outside of an IDE is essential to host it elsewhere. If you followed this tutorial, this standard guide will work for you. If you didn't, look into export or packaging guides for your IDE and language of choice – procedures may vary but the end result is the same.
Purchase a VPS or equivalent service A server is essentially a machine that is always online and running, without you having to worry about anything. To host your bot, you can opt for a VPS which serves this purpose and can be rented from several different providers. Another option would be to purchase a network-capable microcontroller, which come in all different specs and sizes depending on your needs.
You should ensure that all user data remains heavily encrypted at all times in your database to guarantee the privacy of your users. The same concept applies to your local instance, however, this becomes especially important once you transfer your database to a remote server.
Upload your executable/package
Once you have a working ssh connection between your machine and your new server, you should upload your executable and all associated files.
We will assume the runnable jar TutorialBot.jar
and its database dbase.db
are currently in the /TBot
folder.
$ scp -r /TBot/ username@server_ip:/bots/TBotRemote/
Run your application
Depending on which language you chose, you might have to configure your server environment differently. If you chose Java, you just need to install a compatible JDK.
$ apt install openjdk-17-jre
$ java -version
If you did everything correctly, you should see a Java version as the output, along with a few other values. This means you're ready to run your application.
Now, to run the executable:
$ cd /bots/TBotRemote/
$ java -jar TutorialBot.jar
Your bot is now online and users can interact with it at any time.
To streamline and modularize this process, you could employ a specialized docker container or equivalent service. If you followed along in one of the equivalent examples (C#, Python, Go and TypeScript) you can find a detailed set of instructions to export and run your code here.
Last updated