Ir al contenido principal

Telegram

Se puede controlar la raspberry mediante comandos ejecutados desde la app "telegram", usando  para ello los BOTS. Para ello deberemos escribir un programa en Python el cual guardaremos en la raspi.

¿Qué son los bots?. 
Son aplicaciones que cualquier usuario puede hacer, que permiten hacer muchas cosas distintas, los hay de noticias, dibujos, música. En nuestro caso los usaremos para controlar nuestra raspberry pi. Me baso en las ideas de otros autores citados abajo en la fuentes.

Va a incluir:
  • Control de una webcam (fotos, timelapse).
  • Información sobre el estado de la Raspberry (ram, hd, temperatura y cpu).
  • Envío de comandos de sistema (como en una terminal).
  • Comprobación de seguridad para restringir el acceso a ciertas partes.(comandos y el uso de raspicam).
 --------------------------------------------------------------------
NOTA: Algunas versiones de Raspian no tiene PIP. Por ejemplo al instalar Raspian con Octoprint, no lo trae.

PIP está instalada en Raspbian Jessie (pero no en Raspbian Wheezy o Jessie Lite). 
Tú puedes instalarla usando apt el siguiente comando:

sudo apt-get install python3-pip
Para la versión de Python 2 :

sudo apt-get install python-pip
pip3 instalalos módulos  para Python 3, y  pip  Instala los módulos para Python 2.

https://www.raspberrypi.org/documentation/linux/software/python.md
-------------------------------------------------------------------------

1)  Instalamos la App de Telegram en la Raspi.

Nos conectamos por SSH a la Raspberry Pi

OPCIÓN A
Instalación del módulo:
sudo pip install pyTelegramBotAPI 


OPCIÓN B
Descargamos la App de Telegram y la instalamos, a través del repositorio de Github:

 
git clone https://github.com/eternnoir/pyTelegramBotAPI.git
cd pyTelegramBotAPI
python setup.py install

Si te da error instalar
apt-get install python-setuptools



2) Creamos un Bot en Telegram
Esto pemite que telegram se comunique con tu ordenador y cree una vía de acceso online para ejecutar órdenes en un script que tengas corriendo en tu PC, en mi caso la Raspberry Pi. Este script debe estar activo "corriendo" y debe tener el Api token para validar la conexión, más adelante lo explico mejor.
Desde tú teléfono móvil u otro ordenador con una cuenta activa de Telegram, crearas un BOT "enlace entre dos o más dispositivos vía http". Para ello escribiremos BotFather en la pestaña "Search" (@botfather)




Nos aparecerá  esta pantalla



pinchamos en START, abajo de la pantalla. 




Escribiremos en "writer a message"
/newboot


Le asignamos un nombre al bot, debes tener la precaución que termine en bot. Si está registrado deberás cambiar el nombre o utilizar el que te sugiera BotFaher, después deberás darle el nombre del usuario.



El bot se llamará: @elnombrequequieras_bot
Debe terminar siempre con bot.

Use this token to access the HTTP API:
xxxxxxxxxxxxxxx (está tapado por seguridad).

¿Qué es el Token?
Cada bot tiene un único número de identificación al ser creado. Este podrá ser modificado a posteriori si es necesario. Es generado por Telegram. Este te permite acceder al boot desde el código escrito en python en la raspi.

3) Para que el bot muestre los comandos que tenemos en la ayuda, tenemos que irnos nuevamente a @BotFather y usar la opción

Elegimos el bot, escribimos
/setcommands
Usa siempre "/" al inicio de cada orden

Aparecerá:
command1 - Description
command2 - Another description

Reemplazamos por:
start - Arranca el bot
ayuda - Muestra la ayuda
exec - Ejecuta como la bash

4) Creación de un archivo python, desde nano u otro editor de texto

Este archivo será creado desde un editor de texto y se guardará en la raspberry pi, en el directorio que desees. Para ejecutarlo bastará ejecutar en el bash:
python chito_telegram.py

Ejemplo 1

El siguiente código ha sido extraído de: Jonathan Fernandez. (ver fuentes) y he modificado algunas cosas.
He colocado como comentario "SUSTITUIR" los puntos donde deberás modificarlo, ya que dependerá de dos cosas: Api token y ID.

nano

#!/usr/bin/env python
# -*- coding: utf-8 -*- 
 
#chito_telegram.py ###### lo podras guardar donde quieras
 
import telebot
from telebot import types
import time
import os

TOKEN = "TOKEN"  # SUSTITUIR

userStep = {}
knownUsers = []

commands = {
              'start': 'Arranca el bot',
              'ayuda': 'Comandos disponibles',
              'exec': 'Ejecuta un comando'
}

menu = types.ReplyKeyboardMarkup()
menu.add("RPinfo", "Camara")

cam_menu = types.ReplyKeyboardMarkup()
cam_menu.add("Foto", "Timelapse")
cam_menu.add("Atras")

info_menu = types.ReplyKeyboardMarkup()
info_menu.add("TEMP", "HD")
info_menu.add("RAM", "CPU")
info_menu.add("Atras")


# COLOR TEXTO
class color:
    RED = '\033[91m'
    BLUE = '\033[94m'
    GREEN = '\033[32m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'


# USER STEP
def get_user_step(uid):
    if uid in userStep:
        return userStep[uid]
    else:
        knownUsers.append(uid)
        userStep[uid] = 0
        print(color.RED + " [¡] ¡¡NUEVO USUARIO!!" + color.ENDC)


# LISTENER
def listener(messages):
    for m in messages:
        if m.content_type == 'text':
            print("[" + str(m.chat.id) + "] " + str(m.chat.first_name) + ": " + m.text)

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


# START
@bot.message_handler(commands=['start'])
def command_start(m):
    cid = m.chat.id
    userStep[cid] = 0
    bot.send_message(cid, "Wake up " + str(m.chat.first_name) + "...")
    time.sleep(1)
    bot.send_message(cid, "Bienvenido a mi botcam...")
    time.sleep(1)
    bot.send_message(cid, "Pasa adelante...\n", reply_markup=menu)


# AYUDA
@bot.message_handler(commands=['ayuda'])
def command_help(m):
    cid = m.chat.id
    help_text = "Grabar sesion: TermRecord -o /tmp/botlog.html\n"
    help_text += "Comandos disponibles: \n"
    for key in commands:
        help_text += "/" + key + ": "
        help_text += commands[key] + "\n"
    bot.send_message(cid, help_text)


# EXEC COMANDO
@bot.message_handler(commands=['exec'])
def command_exec(m):
    cid = m.chat.id
    if cid == "ID":  # SUSTITUIR
        bot.send_message(cid, "Ejecutando: " + m.text[len("/exec"):])
        bot.send_chat_action(cid, 'typing')
        time.sleep(2)
        f = os.popen(m.text[len("/exec"):])
        result = f.read()
        bot.send_message(cid, "Resultado: " + result)
    else:
        bot.send_message(cid, " ¡¡PERMISO DENEGADO!!")
        print(color.RED + " ¡¡PERMISO DENEGADO!! " + color.ENDC)


# MENU PRINCIPAL
@bot.message_handler(func=lambda message: get_user_step(message.chat.id) == 0)
def main_menu(m):
    cid = m.chat.id
    text = m.text
    if text == "RPinfo":  # RPINFO
        bot.send_message(cid, "Informacion disponible:", reply_markup=info_menu)
        userStep[cid] = 1
    elif text == "Camara":  # CAMARA
        bot.send_message(cid, "Opciones de la camara:", reply_markup=cam_menu)
        userStep[cid] = 2
    elif text == "Atras":  # ATRAS
        userStep[cid] = 0
        bot.send_message(cid, "Menu Principal:", reply_markup=menu)
    else:
        command_text(m)


# MENU INFO
@bot.message_handler(func=lambda message: get_user_step(message.chat.id) == 1)
def info_opt(m):
        cid = m.chat.id
        txt = m.text
        if txt == "TEMP":  # TEMP
            bot.send_message(cid, "[+] TEMPERATURAS")
            print(color.BLUE + "[+] TEMPERATURAS" + color.ENDC)
            # cpu temp
            tempFile = open( "/sys/class/thermal/thermal_zone0/temp" )
            cpu_temp = tempFile.read()
            tempFile.close()
            cpu_temp = round(float(cpu_temp)/1000)
            bot.send_message(cid, "  [i]   CPU: %s" % cpu_temp)
            print(color.GREEN + " [i] CPU: %s" % cpu_temp + color.ENDC)
            # gpu temp
            gpu_temp = os.popen('/opt/vc/bin/vcgencmd measure_temp').read().split("=")[1][:-3]
            bot.send_message(cid, "  [i]   GPU: %s" % gpu_temp)
            print(color.GREEN + " [i] GPU: %s" % gpu_temp + color.ENDC)
        elif txt == "HD":  # HD
            bot.send_message(cid, "[+] DISCO DURO")
            print(color.BLUE + "[+] DISCO DURO" + color.ENDC)
            bot.send_message(cid, "  [i]   Total: %s" % diskSpace()[0])
            print(color.GREEN + " [i] Total: %s" % diskSpace()[0] + color.ENDC)
            bot.send_message(cid, "  [i]   Usado: %s" % diskSpace()[1])
            print(color.GREEN + " [i] Usado: %s" % diskSpace()[1] + color.ENDC)
            bot.send_message(cid, "  [i]   Disponible: %s" % diskSpace()[2])
            print(color.GREEN + " [i] Disponible: %s" % diskSpace()[2] + color.ENDC)
        elif txt == "RAM":  # RAM
            bot.send_message(cid, "[+] MEMORIA RAM")
            print(color.BLUE + "[+] MEMORIA RAM" + color.ENDC)
            bot.send_message(cid, "  [i]   Total: %s" % ramInfo()[0])
            print(color.GREEN + " [i] Total: %s" % ramInfo()[0] + color.ENDC)
            bot.send_message(cid, "  [i]   Usado: %s" % ramInfo()[1])
            print(color.GREEN + " [i] Usado: %s" % ramInfo()[1] + color.ENDC)
            bot.send_message(cid, "  [i]   Disponible: %s" % ramInfo()[2])
            print(color.GREEN + " [i] Disponible: %s" % ramInfo()[2] + color.ENDC)
        elif txt == "CPU":  # CPU
            bot.send_message(cid, "[+] CPU")
            print(color.BLUE + "[+] CPU" + color.ENDC)
            cpu = os.popen('mpstat | grep -A 5 "%idle" | tail -n 1 | awk -F " " \'{print 100 - $ 12}\'a').read()
            bot.send_message(cid, "  [i]   Usado: %s" % cpu)
            print(color.GREEN + " [i] Usado: %s" % cpu + color.ENDC)
        elif txt == "Atras":  # ATRAS
            userStep[cid] = 0
            bot.send_message(cid, "Menu Principal:", reply_markup=menu)
        else:
            command_text(m)


# MENU CAMARA
@bot.message_handler(func=lambda message: get_user_step(message.chat.id) == 2)
def cam_opt(m):
        cid = m.chat.id
        text = m.text
        if cid == "ID":  # SUSTITUIR
            if text == "Foto":  # FOTO
                bot.send_message(cid, "Tomando foto ...")
                bot.send_chat_action(cid, 'upload_photo')
                foto = "/tmp/" + (time.strftime("%H%M%S-%d%m%y")) + ".jpeg"
                os.system('fswebcam -d /dev/video0 -r 640x480 --no-banner %s' % foto)
                bot.send_photo(cid, open(foto, 'rb'))
                print(color.BLUE + " [i] Foto enviada!!" + color.ENDC)
            elif text == "Timelapse":  # TIMELAPSE
                bot.send_message(cid, "Nº Fotos?: ")
                bot.register_next_step_handler(m, timelapse)
            elif text == "Atras":  # ATRAS
                userStep[cid] = 0
                bot.send_message(cid, "Menu Principal:", reply_markup=menu)
            else:
                command_text(m)
        else:
            bot.send_message(cid, " ¡¡PERMISO DENEGADO!!")
            print(color.RED + " ¡¡PERMISO DENEGADO!! " + color.ENDC)


# INFO HD
def diskSpace():
    p = os.popen("df -h /")
    i = 0
    while 1:
        i += 1
        line = p.readline()
        if i == 2:
            return(line.split()[1:5])


# INFO RAM
def ramInfo():
    p = os.popen('free -o -h')
    i = 0
    while 1:
        i += 1
        line = p.readline()
        if i == 2:
            return(line.split()[1:4])


# TIMELAPSE
def timelapse(m):
    cid = m.chat.id
    start = 0
    end = m.text
    print(color.BLUE + "Nº FOTOS: " + str(end) + color.ENDC)
    if end.isdigit():
        bot.send_message(cid, "Comienza la captura de fotos...")
        print(color.BLUE + "[+] Comienza la captura de fotos..." + color.ENDC)
        while start < int(end):
            print(color.BLUE + " [i] Capturando imagen %i" % start + color.ENDC)
            bot.send_chat_action(cid, 'typing')
            os.system("fswebcam -i 0 -d /dev/video0 -r 640x480 -q --no-banner /tmp/%d%m%y_%H%M%S.jpg")
            start = start + 1
            time.sleep(10)
        print(color.BLUE + "[-] Proceso TIMELAPSE finalizado!!" + color.ENDC)
        bot.send_message(cid, "Proceso TIMELAPSE finalizado!!")

        markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
        markup.add('SI', 'NO')
        msg = bot.reply_to(m, "Enviar fotos?: ", reply_markup=markup)
        bot.register_next_step_handler(msg, tarFotos)
    else:
        bot.send_message(cid, "Introduce numero de fotos")
        bot.register_next_step_handler(m, timelapse)
        return


# TAR FOTOS
def tarFotos(m):
    cid = m.chat.id
    msg = m.text
    if msg == "SI":
        bot.send_message(cid, "Comprimiendo fotos...")
        print(color.BLUE + "[+] Comprimiendo fotos..." + color.ENDC)
        bot.send_chat_action(cid, 'typing')
        os.system("tar -cvf /tmp/timelapse.tar /tmp/*.jpg")
        bot.send_message(cid, "Fotos comprimidas. Enviando...")
        bot.send_chat_action(cid, 'upload_document')
        tar = open('/tmp/timelapse.tar', 'rb')
        bot.send_document(cid, tar)
        print(color.BLUE + " [+] Fotos Enviadas!!" + color.ENDC)
        userStep[cid] = 0
        bot.send_message(cid, "Menu Principal:", reply_markup=menu)
    else:
        userStep[cid] = 0
        bot.send_message(cid, "Menu Principal:", reply_markup=menu)


# FILTRAR MENSAJES
@bot.message_handler(func=lambda message: True, content_types=['text'])
def command_text(m):
    cid = m.chat.id
    if (m.text.lower() in ['hola', 'hi', 'buenas', 'buenos dias']):
        bot.send_message(cid, 'Muy buenas, ' + str(m.from_user.first_name) + '. Me alegra verte de nuevo.', parse_mode="Markdown")
    elif (m.text.lower() in ['adios', 'aios', 'adeu', 'ciao']):
        bot.send_message(cid, 'Hasta luego, ' + str(m.from_user.first_name) + '. Te echaré de menos.', parse_mode="Markdown")


print 'Corriendo...'
bot.polling(none_stop=True)

Recuerda guardarlo con la extensión .py (cambiará de color).

¿Cómo obtener el ID?

Todo usuario de Telegram tiene una ID única que lo identifica, al correr el script de phyton y entablar una conversación con el bot (chat normal individual) podrás ver el ID único (o cualquier ID que reciba de cualquier usuario) cuando se la mande un comando, esto será visible en la shell sobre la raspberry.

El ID puede cambiar
El ID cambia si es un chat individual (ID con números positivos) o en un chat grupal (ID con números negativos).




Otro ejemplo
Original de Jesús Camacho, (ver referencias web)


 #-*- coding: utf-8 -*-

"""Este programa permite ejecutar ordenes de bash desde python, conectas tu raspberry a internet utilizando como interfase de conexión los bots de telegram"""

import telebot # Librería de la API del bot.
from telebot import types # Tipos para la API del bot.
import time # Librería para hacer que el programa que controla el bot no se acabe.
import random
import datetime
import token
import os
import commands

TOKEN = 'xxxxxxxxxxxxxxxxxxxxxx' #Nuestro token del bot
bot = telebot.TeleBot(TOKEN) # Creamos el objeto de nuestro bot.

#############################################
#Listener
def listener(messages): # Con esto, estamos definiendo una función llamada 'listener', que recibe como parámetro un dato llamado 'messages'.
    for m in messages: # Por cada dato 'm' en el dato 'messages'
        cid = m.chat.id # Almacenaremos el ID de la conversación.
        if m.content_type == 'text':
            print "[" + str(cid) + "]: " + m.text # Y haremos que imprima algo parecido a esto -> [52033876]: /start

bot.set_update_listener(listener) # Así, le decimos al bot que utilice como función escuchadora nuestra función 'listener' declarada arriba.


#############################################
#Funciones disponibles
@bot.message_handler(commands=['help'])
def command_ayuda(m):
    cid = m.chat.id
    bot.send_message( cid, "Comandos Disponibles: /help, /temp, /impresora, /sala")


#######    Esta estructura permite ejecutar comandos bash desde python


@bot.message_handler(commands=['temp'])
def command_temp(m):
    cid = m.chat.id
    #temp = os.system('sudo /opt/vc/bin/vcgencmd measure_temp')
    temp = commands.getoutput('sudo /opt/vc/bin/vcgencmd measure_temp')   
    bot.send_message(cid, temp)

@bot.message_handler(commands=['sala'])
def command_temp(m):
        cid = m.chat.id
    sala = commands.getoutput('python sala.py')  #ejecuta sala.py  
        bot.send_message(cid, sala)

@bot.message_handler(commands=['impresora'])
def command_temp(m):
        cid = m.chat.id
        impresora = commands.getoutput('python impresora.py')  # ejecuta impresora.py  
        bot.send_message(cid, impresora)

#############################################



#Peticiones
bot.polling(none_stop=True) # Con esto, le decimos al bot que siga funcionando incluso si encuentra algun fallo.





Hacer el bot más seguro, solo podrás usarlo tú

Si una persona sabe el nombre de tú bot, podría ejecutarlo a menos que lo hagas más seguro.
Hay que agregar este script a cada comando para garantizar que solo nosotros podamos usarlo.

@bot.message.handler(commands=['test'])
def command_test(m):
cid = m.chat.id
uid = m.from_user.id
if  uid != user.user_id:
bot.send_message(cid, "No puedes usar este bot")
return
bot.send_message(cid, "Esto es una prueba")




Fuentes

Comentarios