DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Telegram Bots for Begginers
Maksym Zavalniuk
Maksym Zavalniuk

Posted on

Telegram Bots for Begginers

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}"}'
Enter fullscreen mode Exit fullscreen mode
  • 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}'
}
Enter fullscreen mode Exit fullscreen mode
  • 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}"}
Enter fullscreen mode Exit fullscreen mode

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.

  1. Install the package. We can do this by pip. Also, other ways to download the library are listed here.

    pip install -U aiogram
    
  2. Let's create a project structure. We need two python files: config.py and main.py. It will look like this:

    Folder structure

  3. 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.

    Getting the token

  4. Write some start code. First of all, copy the token from the BotFater and paste it to the config.py. We use the os 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 the logging module, aiogram objects, and API_TOKEN from config.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)
    
  5. In summary, we have two files:

  • config.py
import os

API_TOKEN = os.getenv('TOKEN', '5135575762:AAF9vytvlcuL-hruAHHMgZ3G2WvLpbZXMSI')
Enter fullscreen mode Exit fullscreen mode
  • 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)
Enter fullscreen mode Exit fullscreen mode

What we got in the end?🀩

Let's run our bot with the command in the terminal:

python main.py
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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 😺

Final result

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! ❀️ ❀️ ❀️

Top comments (3)

Collapse
 
sectasy0 profile image
Piotrek M.

Great tutorial! If you want to internationalize your bots you can use my library written in Python. github.com/sectasy0/pyi18n

Collapse
 
mezgoodle profile image
Maksym Zavalniuk Author

Thank you very much! This is my first article, and I am infinitely grateful to you. Earlier I tried to internationalize my bot, but it didn't work. I will definitely check out your library.

Collapse
 
sectasy0 profile image
Piotrek M.

That's great, I would be grateful for a little feedback after using my library, maybe there is something that can be improved or done better, please create an issue

🌚 Friends don't let friends browse without dark mode.

Good news! You can update to dark mode in your DEV settings.