Skip to content

Instantly share code, notes, and snippets.

@manuelep
Last active November 3, 2023 09:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save manuelep/9ccdf45dc7b688bb66b410fbd6581026 to your computer and use it in GitHub Desktop.
Save manuelep/9ccdf45dc7b688bb66b410fbd6581026 to your computer and use it in GitHub Desktop.
Pacchettizzazione di un bot telegram in Python

Pacchettizzazione di un bot telegram in Python

_ _

Introduzione

Conosciamo Telegram spesso come semplice alternativa ad altri sistemi di instant messaging talvolta più diffusi come WhatsApp, Facebook Messanger o Signal (giusto per citare almeno un altro concorrente dall'ambito del software libero). Ma curiosando tra la documentazione si scopre che Telegram offre altri servizi che lo distinguono dall'offerta concorrente come la possibilità di implementare dei propri bot custom.

Cosa è un Bot Telegram?

I Bot Telegram possono essere intesi come applicazioni che possono essere eseguite su un qualsiasi server o pc e con le quali è possibile interagire interamente attraverso l'app di Telegram.

Gli utenti interagiscono con i bot attraverso una interfaccia flessibile che supporta qualsiasi tipo di attività o servizio, come semplice testo, immagini o allegati di vario tipo fino a posizioni e tracciati di percorsi sfruttando così le potenzialità dei device che ogni giorno ci portiamo in tasca.

In certi limiti un Bot Telegram può essere inteso anche come alternativa semplificata ad affrontare l'implementazione di una vera e propria applicazione web la cui componente client è costituita dall'app sollevando gli sviluppatori anche dalle complicazioni della fase di deploy. In pochi passaggi è possibile in questo modo raggiungere qualunque utente Telegram fornendo servizi con il solo limite della nostra fantasia.

Il Bot si presenta come un canale a cui l'utente deve iscriversi volontarisamente e nel quale i messaggi multimediali, che rappresentano l'input, degli utenti iscritti, vengono ricevuti e processati secondo la logica che noi implementiamo. L'output in uscita dal processamento può essere quindi anche indirizzato agli estessi utenti attraverso la stessa interfaccia.

I bot Telegram necessitano di una registrazione per la quale qui rimandiamo alla documentazione ufficale e da cui si ottiene un token identificativo fondamentale che vedremo di seguito come deve essere usato.

Perché un Bot Telegram in Python?

Anche nel caso dei Bot Telegram, come in molti altri casi, esiste una libreria Python dedicata che ci fornisce strumenti utili all'interfacciamento con i servizi di API che permettono l'implementazione di Bot, permettendoci di dedicarci unicamente alla programmazione più di alto livello e concentrarci così sulla parte di logica e automazione propria dell'ambito di applicazione che abbiamo in mente.

La libreria in questione è aiogram e rappresenta anche un esempio molto avanzato di strumenti sviluppati per il web che sfrutta le potenzialità di perfomance offerte dalla programmazione asincrona nativamente disponibili a partire dalla versione 3 di Python.

L'importanxza del "packaging"

Sebbene l'approccio semplice legato alla curva di apprendimento del linguaggio Python sia di fatto uno dei suoi punti di forza, grazie al quale spesso è sufficiente la stesura di piccoli script per fare pratica e testare procedure, la "pacchettizzazione" rappresenta uno dei principali salti di qualità che possiamo fare nella produzione di codice. Pacchettizzare (in inglese Packaging) in ambito informatico significa adottare una serie di standard nella scrittura del codice, di fatto non necessari, ma molto utili per rendere il nostro lavoro riutilizzabile e pronto per essere condiviso risolvendo in maniera elegante i problemi di dipendenze software e l'installazione di prerequisiti.

Gli esempi di codice Python che quindi si possono trovare in rete, nella totalità dei casi, vengono forniti sotto forma di semplici script per non appensantire le notazioni usate e anche la documentazione della libreria aiogram non sfugge a questa logica.

Vediamo quindi i passi fondamentali per pacchettizzare un Bot Telegram a partire dall'esempio base della libreria che implementa un semplice caso di automazione che, nella fattispecie, come una eco, ripete i nostri messaggi identici a come li inviamo.

Mettiamoci all'opera

Una possibile struttura base di file e cartelle come proposto da documentazione ufficiale Python è quella rappresentata qui di seguito.

** mybot ** /
├── LICENSE
├── pyproject.toml
├── README.md
├── src /
│   └── ** mybot ** /
│       ├── __init__.py
│       |── ** bot.py **
|       └── ...
└── tests /

In questa struttura possono essere adattati i nomi evidenziati da ** (doppio asterisco) mentre la cartella tests è opzionale e non contemplata nelle istruzioni che seguono.

Compiliamo per prima cosa il file pyproject.toml con il seguente codice adattando le meta informazioni al nostro caso come indicato nella documentazione di python.

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "mybot"
version = "0.0.1"
authors = [
  { name="Example Author", email="author@example.com" },
]
description = "My Telegram bot"
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]
dependencies = [
    "aiogram == 3.1.1",
    "python-dotenv",
    # ...
]

# [project.urls]
# "Homepage" = "https://github.com/pypa/sampleproject"
# "Bug Tracker" = "https://github.com/pypa/sampleproject/issues"

[project.scripts]
run-botgram = "mybot.bot:run"

Sarà importante mantenere aggiornata la lista delle dipendenze del nostro pacchetto contenuta nella variabile dependencies. In questo caso sono necessari almeno le librerie aiogram e python-dotenv di cui vedremo l'uso di quest'ultima in seguito.

Copiamo ora il codice seguente nel file bot.py che, con ben poche differenze non è altro che il codice di partenza suggerito dalla documentazione di aiogram.

import asyncio
import logging
import sys
from os import getenv

from . import settings
from .common import logger

from aiogram import Bot, Dispatcher, Router, types
from aiogram.enums import ParseMode
from aiogram.filters import CommandStart
from aiogram.types import Message
from aiogram.utils.markdown import hbold
from aiogram.utils.token import TokenValidationError

# Bot token can be obtained via https://t.me/BotFather
TOKEN = settings.BOT_TOKEN

# All handlers should be attached to the Router (or Dispatcher)
dp = Dispatcher()


@dp.message(CommandStart())
async def command_start_handler(message: Message) -> None:
    """
    This handler receives messages with `/start` command
    """
    # Most event objects have aliases for API methods that can be called in events' context
    # For example if you want to answer to incoming message you can use `message.answer(...)` alias
    # and the target chat will be passed to :ref:`aiogram.methods.send_message.SendMessage`
    # method automatically or call API method directly via
    # Bot instance: `bot.send_message(chat_id=message.chat.id, ...)`
    await message.answer(f"Hello, {hbold(message.from_user.full_name)}!")


@dp.message()
async def echo_handler(message: types.Message) -> None:
    """
    Handler will forward receive a message back to the sender

    By default, message handler will handle all message types (like a text, photo, sticker etc.)
    """
    try:
        # Send a copy of the received message
        await message.send_copy(chat_id=message.chat.id)
    except TypeError:
        # But not all the types is supported to be copied so need to handle it
        await message.answer("Nice try!")


async def main() -> None:
    # Initialize Bot instance with a default parse mode which will be passed to all API calls
    bot = Bot(TOKEN, parse_mode=ParseMode.HTML)

    # And the run events dispatching
    await dp.start_polling(bot)

def run() -> None:
    # logging.basicConfig(level=logging.INFO, stream=sys.stdout)
    asyncio.run(main())


if __name__ == "__main__":
    run()

Le due importanti differenze qui introdotte sono la parametrizzazione del token (TOKEN = settings.BOT_TOKEN) e la definizione della funzione run che ci permette di avviare agevolmente il nostro bot una volta installato il pacchetto.

Aggiungiamo per buona prassi di sviluppo la definizione di un logger all'interno di un file parallelo a quello precedente chiamato common.py

import sys
import logging
from . import settings

logger = logging.getLogger(settings.APP_NAME)
formatter = logging.Formatter(
    "%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
)
for item in settings.LOGGERS:
    level, filename = item.split(":", 1)
    if filename in ("stdout", "stderr"):
        handler = logging.StreamHandler(getattr(sys, filename))
    else:
        handler = logging.FileHandler(filename)
    handler.setFormatter(formatter)
    logger.setLevel(getattr(logging, level.upper(), "DEBUG"))
    logger.addHandler(handler)

e definiamo per comodità alcune semplici variabili in settings.py come segue.

import os

# logger settings
# This should be in a separate settings file

from dotenv import load_dotenv

load_dotenv()

APP_NAME = 'mybot'

LOGGERS = [
    "debug:stdout"
]  # syntax "severity:filename" filename can be stderr or stdout

BOT_TOKEN = os.environ.get('BOT_TOKEN')

In questo ultimo codice introdotto vediamo l'uso e l'utilità della libreria dotenv che ci permette di definire da un file di testo valori di variabili di ambiente. In questo modo possiamo predisporre un file .env come segue:

APP_NAME=botgram
BOT_TOKEN=

valorizzando la variabile BOT_TOKEN con il token che abbiamo ottenuto in seguito dalla registrazione del nostro bot. A questo punto è suffiente:

  1. installare la libreria che abbiamo appena creato dalla cartella radice con il comando:
    pip install .
  2. lanciare dalla cartella contenente il file .env il comando:
    python -m mybot.bot.run
  3. iscriverci al bot registrato attraverso l'app Telegram
  4. Interagire con il nostro bot

Happy hacking !!

Sitografia di riferimento

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment