Telegram, contronlando tu servidor hablándole a un BOT

Telegram es un software de mensajería instantánea, similar a Whatsapp pero con muchísima más potencia, seguridad y aplicaciones potenciales. Una de las posibilidades que nos ofrece es la creación de bots, contactos virtuales. Además nos proporciona una API bien documentada a través de la que podemos crear programas que controlen esos bots, procesen sus mensajes y realicen ciertas acciones.

En esta entrada voy a explicar como crear un bot desde cero, y como hacer un pequeño programa en Python, capaz de realizar ciertas acciones sobre nuestro servidor dependiendo de lo que le digamos a través de nuestro móvil.

Creación del Bot BotIjo1

Antes de empezar, podemos visitar la web Bots: An introduction for developers donde se explica bastante detalladamente desde el punto de vista de un desarrollador, lo que son los bots y para que se pueden usar.

El primer paso es abrir Telegram, y en la ventana principal buscar al contacto global BotFather:

pScreenshot_2016-03-17-20-02-07

pScreenshot_2016-03-17-20-04-15

Una vez encontrado hacemos clic sobre él, y podremos mantener una conversación para que nos cree nuestro querido BotIjo1. Pulsamos sobre el botón INICIAR en la parte inferior de la pantalla y empezamos:

pScreenshot_2016-03-17-20-04-24

Lo primero que nos muestra es una lista con los comandos disponibles: crear bot, cambiarle el nombre, generar un token de autorización, cambiar la lista de comandos del bot, eliminarlo, …

pScreenshot_2016-03-17-20-06-07Para crear nuestro BotIjo1, debemos introducir el comando /newbot y BotFather nos pedirá el nombre público del bot y el nombre de usuario del bot (pueden ser iguales, aunque no todos los nombres están disponibles):

 pScreenshot1

El último mensaje que nos devuelve BotFather es extremadamente importante ya que nos indica el token de acceso a la API para programar nuestro bot. Lo necesitaremos cada vez que tengamos que hacer uso del bot, es el token interno de Telegram que identifica a nuestro bot.

Ya tenemos preparado nuestro bot, pero ¿cómo hacemos que un programa se se comunique con nosotros a través de ese bot? Vamos a ello.

Dando personalidad a nuestro bot

Existen varias librerías que nos permiten comunicarnos a través de bot con nuestro sistema. Yo he usado una muy sencilla, hecha en Python que se llama pyTelegramBotAPI. La instalamos desde su repositorio Git:

root@servidor:/tmp# git clone https://github.com/eternnoir/pyTelegramBotAPI
Cloning into 'pyTelegramBotAPI'...
remote: Counting objects: 1324, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 1324 (delta 6), reused 0 (delta 0), pack-reused 1311
Receiving objects: 100% (1324/1324), 874.19 KiB | 561.00 KiB/s, done.
Resolving deltas: 100% (722/722), done.
Checking connectivity... done.
root@servidor:/tmp# cd pyTelegramBotAPI
root@servidor:/tmp/pyTelegramBotAPI# python setup.py install
running install
running bdist_egg
running egg_info
creating pyTelegramBotAPI.egg-info
writing requirements to pyTelegramBotAPI.egg-info/requires.txt
writing pyTelegramBotAPI.egg-info/PKG-INFO
writing top-level names to pyTelegramBotAPI.egg-info/top_level.txt
writing dependency_links to pyTelegramBotAPI.egg-info/dependency_links.txt
writing manifest file 'pyTelegramBotAPI.egg-info/SOURCES.txt'
reading manifest file 'pyTelegramBotAPI.egg-info/SOURCES.txt'
writing manifest file 'pyTelegramBotAPI.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-armv7l/egg
running install_lib
running build_py
creating build
creating build/lib.linux-armv7l-2.7
creating build/lib.linux-armv7l-2.7/telebot
copying telebot/util.py -> build/lib.linux-armv7l-2.7/telebot
copying telebot/types.py -> build/lib.linux-armv7l-2.7/telebot
copying telebot/apihelper.py -> build/lib.linux-armv7l-2.7/telebot
copying telebot/__init__.py -> build/lib.linux-armv7l-2.7/telebot
creating build/bdist.linux-armv7l
creating build/bdist.linux-armv7l/egg
creating build/bdist.linux-armv7l/egg/telebot
copying build/lib.linux-armv7l-2.7/telebot/__init__.py -> build/bdist.linux-armv7l/egg/telebot
copying build/lib.linux-armv7l-2.7/telebot/apihelper.py -> build/bdist.linux-armv7l/egg/telebot
copying build/lib.linux-armv7l-2.7/telebot/types.py -> build/bdist.linux-armv7l/egg/telebot
copying build/lib.linux-armv7l-2.7/telebot/util.py -> build/bdist.linux-armv7l/egg/telebot
byte-compiling build/bdist.linux-armv7l/egg/telebot/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-armv7l/egg/telebot/apihelper.py to apihelper.pyc
byte-compiling build/bdist.linux-armv7l/egg/telebot/types.py to types.pyc
byte-compiling build/bdist.linux-armv7l/egg/telebot/util.py to util.pyc
creating build/bdist.linux-armv7l/egg/EGG-INFO
copying pyTelegramBotAPI.egg-info/PKG-INFO -> build/bdist.linux-armv7l/egg/EGG-INFO
copying pyTelegramBotAPI.egg-info/SOURCES.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
copying pyTelegramBotAPI.egg-info/dependency_links.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
copying pyTelegramBotAPI.egg-info/requires.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
copying pyTelegramBotAPI.egg-info/top_level.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/pyTelegramBotAPI-1.4.2-py2.7.egg' and adding 'build/bdist.linux-armv7l/egg' to it
removing 'build/bdist.linux-armv7l/egg' (and everything under it)
Processing pyTelegramBotAPI-1.4.2-py2.7.egg
Copying pyTelegramBotAPI-1.4.2-py2.7.egg to /usr/local/lib/python2.7/dist-packages
Adding pyTelegramBotAPI 1.4.2 to easy-install.pth file

Installed /usr/local/lib/python2.7/dist-packages/pyTelegramBotAPI-1.4.2-py2.7.egg
Processing dependencies for pyTelegramBotAPI==1.4.2
Searching for pytest
Reading https://pypi.python.org/simple/pytest/
Best match: pytest 2.9.1
Downloading https://pypi.python.org/packages/source/p/pytest/pytest-2.9.1.tar.gz#md5=05165740ea50928e4e971378630163ec
Processing pytest-2.9.1.tar.gz
Writing /tmp/easy_install-58JQHV/pytest-2.9.1/setup.cfg
Running pytest-2.9.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-58JQHV/pytest-2.9.1/egg-dist-tmp-qvnNrl
warning: no previously-included files found matching '_pytest/impl'
warning: no previously-included files matching '*.pyc' found under directory '*'
warning: no previously-included files matching '*.pyo' found under directory '*'
warning: no previously-included files found matching 'appveyor/install.ps1'
warning: no previously-included files found matching 'appveyor.yml'
warning: no previously-included files found matching 'appveyor'
warning: no previously-included files found matching 'ISSUES.txt'
warning: no previously-included files found matching 'HOWTORELEASE.rst'
Adding pytest 2.9.1 to easy-install.pth file
Installing py.test script to /usr/local/bin
Installing py.test-2.7 script to /usr/local/bin

Installed /usr/local/lib/python2.7/dist-packages/pytest-2.9.1-py2.7.egg
Searching for py>=1.4.29
Reading https://pypi.python.org/simple/py/
Best match: py 1.4.31
Downloading https://pypi.python.org/packages/source/p/py/py-1.4.31.tar.gz#md5=5d2c63c56dc3f2115ec35c066ecd582b
Processing py-1.4.31.tar.gz
Writing /tmp/easy_install-dUltGT/py-1.4.31/setup.cfg
Running py-1.4.31/setup.py -q bdist_egg --dist-dir /tmp/easy_install-dUltGT/py-1.4.31/egg-dist-tmp-NYPRCa
Adding py 1.4.31 to easy-install.pth file

Installed /usr/local/lib/python2.7/dist-packages/py-1.4.31-py2.7.egg
Searching for six==1.5.2
Best match: six 1.5.2
Adding six 1.5.2 to easy-install.pth file

Using /usr/lib/python2.7/dist-packages
Searching for requests==2.2.1
Best match: requests 2.2.1
requests 2.2.1 is already the active version in easy-install.pth

Using /usr/lib/python2.7/dist-packages
Finished processing dependencies for pyTelegramBotAPI==1.4.2
root@servidor:/tmp/pyTelegramBotAPI#

Una vez instalado, podemos abrir alguno de los ejemplos que tenemos en la carpeta examples  y vemos lo simple que es la estructura del programa. Basándome en el detailed_example tengo este código, que me perdonen los programadores de Python, pero es la primera vez que toco este lenguaje de programación:

"""
This is a detailed example using almost every command of the API
"""

import telebot
from telebot import types
import time
import os

TOKEN = '180031801:AAFfIv8M62qyAyToYFJcvh5t9gsIcSj5Yhg'

knownUsers = [] # todo: save these in a file,
userStep = {} # so they won't reset every time the bot restarts

commands = { # command description used in the "help" command
             'start': 'Get used to the bot',
             'ayuda': 'Da informacion sobre los comandos disponibles',
             'exec': 'Ejecuta un comando',
             'reboot': 'Reinicia el servidor'
}

hideBoard = types.ReplyKeyboardHide() # if sent as reply_markup, will hide the keyboard

# error handling if user isn't known yet
# (obsolete once known users are saved to file, because all users
# had to use the /start command and are therefore known to the bot)
def get_user_step(uid):
    if uid in userStep:
        return userStep[uid]
    else:
        knownUsers.append(uid)
        userStep[uid] = 0
        print "New user detected, who hasn't used \"/start\" yet"
        return 0

# only used for console output now
def listener(messages):
    """
    When new messages arrive TeleBot will call this function.
    """
    for m in messages:
        if m.content_type == 'text':
            # print the sent message to the console
            print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text


bot = telebot.TeleBot(TOKEN)
bot.set_update_listener(listener) # register listener

# handle the "/start" command
@bot.message_handler(commands=['start'])
def command_start(m):
    cid = m.chat.id
    if cid not in knownUsers:
        knownUsers.append(cid) 
        userStep[cid] = 0
        command_help(m) # show the new user the help page

# help page
@bot.message_handler(commands=['ayuda'])
def command_help(m):
    cid = m.chat.id
    help_text = "Estos son los comandos disponibles: \n"
    for key in commands:
        help_text += "/" + key + ": "
        help_text += commands[key] + "\n"
    bot.send_message(cid, help_text)

# Reinicia servidor
@bot.message_handler(commands=['reboot'])
def command_long_text(m):
    cid = m.chat.id
    bot.send_message(cid, "Voy a reiniciar el servidor...")
    bot.send_chat_action(cid, 'typing')
    time.sleep(3)
    bot.send_message(cid, ".")
    os.system("reboot")

# Ejecuta un comando
@bot.message_handler(commands=['exec'])
def command_long_text(m):
    cid = m.chat.id
    bot.send_message(cid, "Ejecutando: "+m.text[len("/exec"):])
    bot.send_chat_action(cid, 'typing') # show the bot "typing" (max. 5 secs)
    time.sleep(2)
    f = os.popen(m.text[len("/exec"):])
    result = f.read()
    bot.send_message(cid, "Resultado: "+result)


# filter on a specific message
@bot.message_handler(func=lambda message: message.text == "hi")
def command_text_hi(m):
    bot.send_message(m.chat.id, "I love you too!")
    # default handler for every other text


@bot.message_handler(func=lambda message: True, content_types=['text'])
def command_default(m):
    # this is the standard reply to a normal message
    bot.send_message(m.chat.id, "No te entiendo, prueba con /ayuda")

bot.polling()

El código es muy simple (lo podéis descargar desde mi repositorio en GitHub):

  • En la variable API_TOKEN ponemos el token de nuestro bot
  • En la variable commands tenemos los comandos entendibles por el bot
  • Creamos el bot con: bot = telebot.TeleBot(TOKEN)
  • Definimos las funciones que procesaran cada comando con bloques similares a:
    # Reinicia servidor
    @bot.message_handler(commands=['reboot'])
    def command_long_text(m):
        cid = m.chat.id
        bot.send_message(cid, "Voy a reiniciar el servidor...")
        bot.send_chat_action(cid, 'typing')
        time.sleep(3)
        bot.send_message(cid, ".")
        os.system("reboot")
  • Ponemos el bot a trabajar con bot.polling()

 

Ejecutamos nuestro programa en python con

root@servidor:/tmp# python botijo1.py

 

y nos vamos a nuestro móvil, buscamos a BotIjo1 de forma similar a como buscamos a BotFather, y empezamos a hablar con él:

pScreenshot_2016-03-21-12-00-10

Si volvemos a nuestra linea de comandos, donde lanzamos el programa Python, veremos los comandos que se han introducido, a modo de log:

root@servidor:/tmp# python botijo1.py
Jose Maria [13443962]: /start
Jose Maria [13443962]: hola
Jose Maria [13443962]: hi!
Jose Maria [13443962]: /exec ls /tmp
Jose Maria [13443962]: /exec date

Como veis es muy fácil crear un bot, y tiene muchísima potencia, todo depende de la imaginación que tengamos: si tenemos alguna cámara web podemos enviar fotogramas, activar una electroválvula de nuestro jardín, encender luces…

¿Qué se puede mejorar? La seguridad. Para evitar que otros usuarios hablen con vuestro bot, podéis filtrar los mensajes e ignorar todos los que no lleguen con determinado chat ID (m.chat.id en el código, y en el log anterior 13443962), de esta forma evitareis sorpresas… 🙂

Además si quereis que el script se ejecute en cada inicio del sistema, lo podéis hacer con upstart creando el archivo botijo1.conf con este contenido en el directorio /etc/init:

description "BOT Telegram"
author "Jose Maria <jdiaz.bb@gmail.com>"

start on runlevel [2345]

respawn

exec python /opt/bot/botijo1.py

Espero que os haya gustado y os animéis a programar vuestros propios bots, con sus comandos 🙂

 

Actualización 29/05/2016:

Si queréis verificar que solo ciertos usuarios pueden ejecutar comandos lo podéis hacer comprobando el m.chat.id, más o menos así, cambiando lo emborronado, por vuestro código:

chat.id

6 comentarios

Añadir un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.