Maksym Zavalniuk
Posted on August 14, 2022
Short overview π
Telegram has a huge API interface, and the development of bots is currently increasing very quickly and is in great demand. For beginners, this is a nice opportunity to get the first job and a good salary. The question arises: how to start writing a bot?
βIn this post, you'll find information on:
- which language to choose;
- which frameworks to use;
- how to improve bot development;
- what to do next.
How to choose the language? π€
Honestly, write in the language that is closest to you. There is no gold standard. Let's see why. As you know, to get data from an API, you need to write a script that makes HTTP requests. For example, getting data from GitHub API, it will look like this:
- in Python (with requests package)
>>> response = requests.get('https://api.github.com')
>>> response.content
b'{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","notifications_url":"https://api.github.com/notifications","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_url":"https://api.github.com/orgs/{org}","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","team_url":"https://api.github.com/teams","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}'
- in JavaScript (with fetch method)
fetch('https://api.github.com')
.then(response => response.json())
.then(data => {
console.log(data)
})
.catch(error => console.error(error))
// data from the response is:
{
current_user_url: 'https://api.github.com/user',
current_user_authorizations_html_url: 'https://github.com/settings/connections/applications{/client_id}',
authorizations_url: 'https://api.github.com/authorizations',
code_search_url: 'https://api.github.com/search/code?q={query}{&page,per_page,sort,order}',
commit_search_url: 'https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}',
emails_url: 'https://api.github.com/user/emails',
emojis_url: 'https://api.github.com/emojis',
events_url: 'https://api.github.com/events',
feeds_url: 'https://api.github.com/feeds',
followers_url: 'https://api.github.com/user/followers',
following_url: 'https://api.github.com/user/following{/target}',
gists_url: 'https://api.github.com/gists{/gist_id}',
hub_url: 'https://api.github.com/hub',
issue_search_url: 'https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}',
issues_url: 'https://api.github.com/issues',
keys_url: 'https://api.github.com/user/keys',
label_search_url: 'https://api.github.com/search/labels?q={query}&repository_id={repository_id}{&page,per_page}',
notifications_url: 'https://api.github.com/notifications',
organization_url: 'https://api.github.com/orgs/{org}',
organization_repositories_url: 'https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}',
organization_teams_url: 'https://api.github.com/orgs/{org}/teams',
public_gists_url: 'https://api.github.com/gists/public',
rate_limit_url: 'https://api.github.com/rate_limit',
repository_url: 'https://api.github.com/repos/{owner}/{repo}',
repository_search_url: 'https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}',
current_user_repositories_url: 'https://api.github.com/user/repos{?type,page,per_page,sort}',
starred_url: 'https://api.github.com/user/starred{/owner}{/repo}',
starred_gists_url: 'https://api.github.com/gists/starred',
topic_search_url: 'https://api.github.com/search/topics?q={query}{&page,per_page}',
user_url: 'https://api.github.com/users/{user}',
user_organizations_url: 'https://api.github.com/user/orgs',
user_repositories_url: 'https://api.github.com/users/{user}/repos{?type,page,per_page,sort}',
user_search_url: 'https://api.github.com/search/users?q={query}{&page,per_page,sort,order}'
}
- in GoLang (with http package)
package main
import (
"net/http"
"io/ioutil"
"log"
)
func main() {
res, err := http.Get("https://api.github.com")
if err != nil {
log.Fatal(err)
}
// read body
body, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
if res.StatusCode != 200 {
log.Fatal("Unexpected status code", res.StatusCode)
}
log.Printf("Body: %s\n", body)
}
// body from the response is:
Body: {"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","label_search_url":"https://api.github.com/search/labels?q={query}&repository_id={repository_id}{&page,per_page}","notifications_url":"https://api.github.com/notifications","organization_url":"https://api.github.com/orgs/{org}","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_teams_url":"https://api.github.com/orgs/{org}/teams","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","topic_search_url":"https://api.github.com/search/topics?q={query}{&page,per_page}","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}
As you can see from the code execution results, there is no difference. So just choose the language in which you write. π
How to choose the framework? ποΈ
Of course, it's good to write API requests yourself. It reduces the dependency on third-party libraries and allows you to control the behavior of the code more. But when there are more such methods than twenty, it already increases the size of the code. It becomes difficult to manage all the logic. This is where third-party libraries(frameworks) come to the rescue. After choosing a language, you can consider the options of different libraries from the list here, on the official Telegram page. For JavaScript I recommend using node-telegram-bot-api and telegraf. For Python you can take pyTelegramBotAPI and aiogram(highly recommended).
Let's start coding the first bot π±βπ»
Since I mostly write code in Python, I suggest you start developing bots with the aiogram. Aiogram is a pretty simple and fully asynchronous framework for Telegram Bot API written in Python 3.7 with asyncio and aiohttp. It helps you to make your bots faster and simpler.
-
Install the package. We can do this by pip. Also, other ways to download the library are listed here.
pip install -U aiogram
-
Let's create a project structure. We need two python files:
config.py
andmain.py
. It will look like this: -
Getting the token. The token is a string along the lines of
110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw
that is required to authorize the bot and send requests to the Bot API. Keep your token secure and store it safely, it can be used by anyone to control your bot. To create a new one, you need to "talk" to the @BotFather and get a token there. -
Write some start code. First of all, copy the token from the BotFater and paste it to the
config.py
. We use theos
module to work with environment variables. It's a good solution to keep your token secret. While you are in the development stage, the token can be visible to you. But in the production stage, remove it.
import os API_TOKEN = os.getenv('TOKEN', '5135575762:AAF9vytvlcuL-hruAHHMgZ3G2WvLpbZXMSI')
In the
main.py
we need to import thelogging
module,aiogram objects
, andAPI_TOKEN
fromconfig.py
.
import logging from aiogram import Bot, Dispatcher, executor from aiogram.types import Message from config import API_TOKEN
Now let's initialize bot and dispatcher objects:
# Configure logging logging.basicConfig(level=logging.INFO) # Initialize bot and dispatcher bot = Bot(token=API_TOKEN) dp = Dispatcher(bot)
Next step: interaction with bots starts with one command. We can handle commands from users like this:
@dp.message_handler(commands=['start', 'help']) async def handle_start(message: Message) -> Message: return await message.reply("Hi!\nI'm TestBot!")
Also, we add one more handler to catch all text messages:
@dp.message_handler() async def echo(message: Message) -> Message: return await message.answer(message.text)
The last step: run long polling. This command will run our bot to catch all updates from Telegram API every second:
if __name__ == '__main__': executor.start_polling(dp, skip_updates=True)
In summary, we have two files:
-
config.py
import os
API_TOKEN = os.getenv('TOKEN', '5135575762:AAF9vytvlcuL-hruAHHMgZ3G2WvLpbZXMSI')
-
main.py
import logging
from aiogram import Bot, Dispatcher, executor
from aiogram.types import Message
from config import API_TOKEN
# Configure logging
logging.basicConfig(level=logging.INFO)
# Initialize bot and dispatcher
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
@dp.message_handler(commands=['start', 'help'])
async def handle_start(message: Message) -> Message:
return await message.reply("Hi!\nI'm TestBot!")
@dp.message_handler()
async def echo(message: Message) -> Message:
return await message.answer(message.text)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
What we got in the end?π€©
Let's run our bot with the command in the terminal:
python main.py
In the console you will something like this:
INFO:aiogram:Bot: testbot [@testmezgoodlebot]
WARNING:aiogram:Updates were skipped successfully.
INFO:aiogram.dispatcher.dispatcher:Start polling.
Now let's go to the Telegram and start chatting with the bot
To start a chat with the bot, follow the link provided by BotFather.
I hope, you have a result like mine πΊ
Congratulations! You've written your first bot! β¨
Yes, now this is a simple echo-bot, but in future posts, we will cover all aspects of bot development. Then you can already create bots of any complexity. So, wait for my next new posts.
References:
Thank you for reading! β€οΈ β€οΈ β€οΈ
Posted on August 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.