Telegram Bots for Begginers

Maksym Zavalniuk - Aug 14 '22 - - Dev Community

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! โค๏ธ โค๏ธ โค๏ธ

. . . . . . . . .