init repo Python Telegram Bots

This commit is contained in:
jp.av.dev 2021-01-04 20:11:22 -03:00
commit b98f430561
12 changed files with 11547 additions and 0 deletions

115
01-blasterbot/README.md Normal file
View File

@ -0,0 +1,115 @@
## Bot Telegram
Funcionalidad:
- */huhuu* Responde con wav
- */allyb* Responde con png
- */caps* Responde con texto capitalizado, lo pasado como argumento
- */desconocido* Responde con frase aleateoria, a cualquier comando desconocido
- */echo* Devuelve el texto recibido
- */timer_minuto* Activa el trabajo alarma, para ser ejecutado en 1 minuto.
- */alarma* Envia msg a modo de alarma
- **func. ip_alert**: Tarea cada 30 minutos, avisa con MSG si la ip del servidor a cambiado
- Otros trabajos de ejemplo
Libreria [python-telegram-bot](https://pypi.org/project/python-telegram-bot/)
```
pip3 install python-telegram-bot
pip3 install fetchip
```
## Clases Principales, Updater, Dispatcher, Handler
*Extracto de [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Your-first-Bot)*
```
telegram.ext.Updater
telegram.ext.Dispatcher
```
La clase **Updater** continuamente consulta y obtiene nuevas actualizaciones
desde telegram. Las pasa a la clase **Dispatcher**
Al crear un objeto Updater, este crea un objeto Dispatcher.
Estos son vinculados entre si en una **Queue**
Ahora se pueden Registrar **handlers** de diferentes tipos en el Dispatcher,
este último ordenará las actualizaciones obtenidas por Updater,
según los **handlers** registrados.
Estos handlers son entregados a las ***funciones callback*** definidas.
```
telegram.ext.Handler
```
Esta libreria ofrece ***manejadores*** para casi todos los casos de uso comúnes.
En caso de necesitar algo especifico, se puede crear una subclase Handler.
## Crear Bot en Telegram
*[telegram bot](https://core.telegram.org/bots)*
```
@BotFather
/newbot
/setname
/setdescription
/setabouttext
/setuserpic
```
## Estructura del directorio
```
 .
├──  conf
│ ├──  config.cfg
│ └──  last_ipp
├──  media
│ ├──  allyb.png
│ └──  huhuu.wav
├──  blasterbot.py
└──  README.md
```
## Token desde config.cfg
Archivo de configuración ***/conf/config.cfg***
```
[creds]
token = <TOKEN>
user = <ID>
```
## User bajo el que correrá el servicio
User sin home ni login
```
sudo useradd --system --no-create-home --shell=/sbin/nologin my-user-service
```
### Conf de permisos comúnmente utilizados
```
sudo chown -R root:my-user-service /path/to/change
sudo chmod -R 775 /path/to/change
```
### Agregar usuario administrador al grupo del usuario bajo el que corre el servicio
```
usermod -a -G service-group-user admin-user
```
## Servicio Linux
**/etc/systemd/system/bot_telegram.service**
```
[Unit]
Description = Python Telegram Bot Service
Wants = network.target
After = network.target
[Service]
User = my-user-service
Grout = group-my-user-service
WorkingDirectory = /path/to/botelegram
ExecStart = /pat/to/.vitEnv/bin/python3 /path/to/botelegram/bot.py
Restart = always
[Install]
WantedBy = multi-user.target
```
### Probar y acticar servicio
```
sudo systemctl start bot_telegram
Si todo va bien 👌️ :
sudo systemctl enable bot_telegram
```

136
01-blasterbot/blasterbot.py Executable file
View File

@ -0,0 +1,136 @@
""" Implementación Bot Telegram API """
import configparser as cfg
import logging
#import os
import fetchip
import random
import telegram.ext
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
# Credenciales desde archivo de configuración
parser = cfg.ConfigParser()
parser.read('./conf/config.cfg')
token = parser.get('creds', 'token')
user = parser.get('creds', 'user')
# Creación objeto Updater
updater = Updater(token=token, use_context=True)
dispatcher = updater.dispatcher
# Config. modulo de logging
logging.basicConfig(
filename='./conf/bot.log',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
# Funciónes 'callback'
def start(update, context):
""" Envía respuesta de texto (text=string) """
context.bot.send_message(chat_id=update.effective_chat.id, text="Hola hola pianola!!")
def echo(update, context):
""" Envía como respuesta el texto recibido """
context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text)
def caps(update, context):
""" Envia como respuesta el texto recibido, capitalizado """
text_caps = ' '.join(context.args).upper()
context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps)
def huhuu(update, context):
""" Envia respuesta de audio (archivo) """
context.bot.send_audio(chat_id=update.effective_chat.id, audio=open('./media/huhuu.wav', 'rb'))
def desconocido(update, context):
""" Envía respuesta de texto si no reconoce el comando recibido """
respuestas = [
'No puedo ayudarte!',
'No entiendo lo que dices!',
'Mucho no bueno!',
'Un gran poder, conlleva una gran resposabilidad',
'QUITAN TRABAOhjOJOoOHHH!',
'For great justice',
'No siempre lloverá',
'mamá dice q la vida es como una caja de bombones ...',
'Mucho agradezco'
]
resp_desc = random.choice(respuestas)
context.bot.send_message(chat_id=update.effective_chat.id, text=resp_desc)
def allyb(update, context):
""" Envia img como respuesta """
context.bot.send_photo(chat_id=update.effective_chat.id, photo=open('./media/allyb.png', 'rb'))
# Trabajo activado a petición
def alarma(context: telegram.ext.CallbackContext):
""" Envia txt como alarma """
context.bot.send_message(chat_id=context.job.context, text='BEEEEP!!')
def timer_minuto(update: telegram.Update, context: telegram.ext.CallbackContext):
""" Tarea corre un minuto despues de llamarla """
context.bot.send_message(chat_id=update.message.chat_id, text='TIMER Activado: ⏰️ 1 minuto')
context.job_queue.run_once(alarma, 60, context=update.message.chat_id)
# Creación de manejadores de funciones CallBack
start_handler = CommandHandler('start', start)
echo_handler = MessageHandler(Filters.text & (~Filters.command), echo)
caps_handler = CommandHandler('caps', caps)
huhuu_handler = CommandHandler('huhuu', huhuu)
allyb_handler = CommandHandler('allyb', allyb)
timer_handler = CommandHandler('timer', timer_minuto)
descon_handler = MessageHandler(Filters.command, desconocido)
# Agregar manejadores a dispatcher
dispatcher.add_handler(start_handler)
dispatcher.add_handler(echo_handler)
dispatcher.add_handler(caps_handler)
dispatcher.add_handler(huhuu_handler)
dispatcher.add_handler(allyb_handler)
dispatcher.add_handler(timer_handler)
dispatcher.add_handler(descon_handler)
# Cola de trabajos
jobs = updater.job_queue
# Trabajo cada minuto
def cada_minuto(context: telegram.ext.CallbackContext):
""" Ejemplo job cada 1 minuto """
context.bot.send_message(chat_id=user, text='test msg cada minuto')
job_minuto = jobs.run_repeating(cada_minuto, interval=60, first=0)
# Solo una vez, con desfase de 5 seg.
def callback_5(context: telegram.ext.CallbackContext):
""" Aviso inicio bot pasado 5 segundos """
context.bot.send_message(chat_id=user, text='BastardBot Iniciado')
jobs.run_once(callback_5, 5)
# Alerta cambio ip pública, cada 30 minutos
def ip_alert(context: telegram.ext.CallbackContext):
""" Alerta, registra e informa cambio de ip pública """
try:
with open('./conf/last_ipp', 'r') as last_ipp_file:
last_ipp = last_ipp_file.read()
# No shell user
# real_ipp = os.popen('curl -s ifconfig.me').readline()
real_ipp = fetchip.get_public_ip()
if real_ipp != last_ipp:
context.bot.send_message(chat_id=user, text=real_ipp)
with open('./conf/last_ipp', 'w') as archivo:
archivo.write(real_ipp)
except Exception as ex:
report = 'Error IP-alert :' + ex
context.bot.send_message(chat_id=user, text=report)
job_ipp = jobs.run_repeating(ip_alert, interval=1800, first=0)
# Deshabilitar tarea
job_minuto.enabled = False
# Remover tarea
#job_minuto.schedule_removal()
# Iniciar Bot
updater.start_polling()

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

17
02-colgado_bot/README.md Normal file
View File

@ -0,0 +1,17 @@
## Telegram Python Bot + juego 'El Colgado'
Fusion de proyectos compartidos en:
- [youtube/CodinEric](https://www.youtube.com/watch?v=dIEdeRGyWFg)
- [youtube/freeCodeCamp.org](https://www.youtube.com/watch?v=8ext9G7xspg&t=1465s)
Crear archivo de configuración
./*config.py*
```
"""
Almacenamiento de secretos
"""
TOKEN = 'bot-token'
ADMIN = 'user_id'
```

View File

@ -0,0 +1,86 @@
vida6 = """
__________
| |
| 🧐
| _/|\\_
| |
| / \\
| _| |_
|
| ||
| | |
"""
vida5 = """
__________
| |
| |
| 🙄
| _/|\\_
| |_
| | |_
| |
| ||
| | |
"""
vida4 = """
__________
| |
| |
| |
| 😣
| _/|\\_
| |_
| | \\_
| ||
| | |
"""
vida3 = """
__________
| |
| |
| |
| |
| 😖
| /|\\
| |
| ||
| | |
"""
vida2 = """
__________
| |
| |
| |
| |
| |
| 😫
| |
| ||
| | |
"""
vida1 = """
__________
| |
| |
| |
| |
| |
| |
| 🤬
| ||
| | |
"""
vida0 = """
__________
| |
| |
| |
| |
| | 👻
| |
| |
| ||
| | |
"""
anim = [vida0, vida1, vida2, vida3, vida4, vida5, vida6]

101
02-colgado_bot/colgabot.py Executable file
View File

@ -0,0 +1,101 @@
""" Implementación Bot Telegram API """
import logging
import random
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters)
import fetchip
import config
import constantes
from sesion_colgado import SesionUsuario
# Credenciales desde archivo de configuración
TOKEN = config.TOKEN
ADMIN = config.ADMIN
MSG_INICIO = ' 🦀 @BlastardBot iniciado 🤖 '
LOG_FILE = 'bot.log'
user_ses = SesionUsuario()
# Creación objeto Updater
updater = Updater(token=TOKEN, use_context=True)
def main():
"""
Agrega maneadores de funciones callback a updater, inicia log y bot.
Bot queda a la espera de recibir algun mensaje.
"""
# Ejecuta start al recibir cualquier mensaje del usuario
#updater.dispatcher.add_handler(MessageHandler(Filters.regex('.*'),start))
# Ejecuta start al iniciar chat con bot, al recibir comando '/start'
updater.dispatcher.add_handler(CommandHandler('start', start))
# Ejecuta start al recibir cualquier mensaje, excepto '/comandos'
#updater.dispatcher.add_handler(MessageHandler(Filters.text & (~Filters.command), start))
updater.dispatcher.add_handler(CommandHandler('huhuu', huhuu))
updater.dispatcher.add_handler(CommandHandler('allyb', allyb))
# Ejecuta desconocido la recibir un comando desconocido '/comando_desconocido'
updater.dispatcher.add_handler(MessageHandler(Filters.command, desconocido))
inicia_log()
updater.start_polling() # Iniciar Bot
updater.bot.send_message(chat_id=ADMIN, text=MSG_INICIO)
updater.idle() # En espera
def start(update, context):
"""
Crea diccionario usuario, registro en log y envía mensaje de inicio
"""
user_id = update.message.from_user.id
user_name = update.message.from_user.username
user_log = f"USER [{user_name}] - ID [{user_id}] ha inicado el bot"
logging.info(user_log)
# Manejador para start con cualquier palabra que no empiece con "/"
updater.dispatcher.add_handler(MessageHandler(Filters.text & (~Filters.command), jugar))
jugar(update, context)
def jugar(update, context):
reply = user_ses.accion(update.message.text)
update.message.reply_text(reply["msg"], reply_markup=reply["reply_markup"])
def huhuu(update, context):
""" Envia respuesta de audio (archivo) """
context.bot.send_audio(chat_id=update.effective_chat.id, audio=open('./media/huhuu.wav', 'rb'))
def allyb(update, context):
""" Envia img como respuesta """
context.bot.send_photo(chat_id=update.effective_chat.id, photo=open('./media/allyb.png', 'rb'))
def desconocido(update, context):
""" Envía respuesta de texto si no reconoce el comando recibido """
resp_desc = random.choice(constantes.RANDOM_RESP)
context.bot.send_message(chat_id=update.effective_chat.id, text=resp_desc)
def inicia_log():
""" Config. modulo de logging """
logging.basicConfig(
filename = LOG_FILE,
format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level = logging.INFO
)
logging.info(MSG_INICIO)
# Cola de trabajos
jobs = updater.job_queue
# Alerta cambio ip pública, cada 30 minutos
def ip_alert(context: telegram.ext.CallbackContext):
""" Alerta, registra e informa cambio de ip pública """
try:
with open('./conf/last_ipp', 'r') as last_ipp_file:
last_ipp = last_ipp_file.read()
# No shell user
# real_ipp = os.popen('curl -s ifconfig.me').readline()
real_ipp = fetchip.get_public_ip()
if real_ipp != last_ipp:
context.bot.send_message(chat_id=ADMIN, text=real_ipp)
with open('./conf/last_ipp', 'w') as archivo:
archivo.write(real_ipp)
except ConnectionError as ex:
report = 'Error IP-alert :' + ex
context.bot.send_message(chat_id=ADMIN, text=report)
job_ipp = jobs.run_repeating(ip_alert, interval=1800, first=0)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,58 @@
"""
Constantes
"""
BOTONES = [
['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Ñ'],
['Z', 'X', 'C', 'V', 'B', 'N', 'M']
]
RANDOM_RESP = [
'No puedo ayudarte!',
'No entiendo lo que dices!',
'Mucho no bueno!',
'Un gran poder, conlleva una gran resposabilidad',
'QUITAN TRABAOhjOJOoOHHH!',
'For great justice',
'No siempre lloverá',
'mamá dice q la vida es como una caja de bombones ...',
'Mucho agradezco'
]
CLASES_RPG = [
"Barbaro",
"Bardo",
"Clerigo",
"Duida",
"Luchador",
"Monje",
"Paladin",
"Soldado",
"Embustero",
"Brujo",
"Hechizero"
]
RAZAS = [
"Humano-SemiOrco",
"MitadElfo-Elfo",
"Enano-Humano",
"Elfo",
"Humano",
"Draco",
"Elfo-Humano",
"Humano-Demonio",
"Demonio",
"MitadElfo",
"Duende"
]
STAT_NAME = [
"str",
"dex",
"int",
"wis",
"con",
"cha"
]

View File

@ -0,0 +1,146 @@
"""
Implementacion juego colgado usando estados
"""
import random
from telegram import ReplyKeyboardMarkup
from palabras import instrumentos, verbos, alfabeto
from anim_colgado import anim
from constantes import BOTONES
class EstadoSelec:
"""
Seleciona lista de palabras
"""
def accion(self, msg, user_data):
user_data.vidas = 0
user_data.tipo_palabra = ""
user_data.palabra_juego = ""
user_data.letras_palabra = set()
user_data.letras_usadas = set()
teclado = ReplyKeyboardMarkup([["verbos"], ["instrumentos"]], one_time_keyboard=True)
self.next = EstadoJugar()
info = "Inciando juego 🤹 \n\n - - \'El Colgado\' - -\n\nElige la palabra a encontrar:\n Verbos o Instrumentos Musicales."
ret = {
"msg": info,
"reply_markup": teclado
}
return ret
class EstadoJugar:
"""
Asignación variables de juego
"""
def accion(self, msg, user_data):
if msg == "verbos" or msg == "instrumentos":
user_data.tipo_palabra = msg
user_data.vidas = 6
palabra = get_palabra(msg)
user_data.palabra_juego = palabra
user_data.letras_palabra = set(palabra)
lista_palabra = [letra if letra in user_data.letras_usadas else '_' for letra in user_data.palabra_juego]
if user_data.tipo_palabra == "instrumentos":
palabra_en_pantalla = '\nInstrumento Musical: ' + ' '.join(lista_palabra)
elif user_data.tipo_palabra == "verbos":
palabra_en_pantalla = '\nVerbo : ' + ' '.join(lista_palabra)
estatus = '\nIntentos restantes ['+ str(user_data.vidas) +']'+' - Letras usadas: '+''.join(user_data.letras_usadas)
info = anim[6] + estatus + palabra_en_pantalla + "\nEnviame una letra"
self.next = EstadoValidaLetra()
teclado = ReplyKeyboardMarkup(BOTONES, one_time_keyboard=True)
ret = {
"msg": info,
"reply_markup": teclado
}
else:
teclado = ReplyKeyboardMarkup([["verbos"], ["instrumentos"]], one_time_keyboard=True)
self.next = EstadoJugar()
ret = {
"msg": f"\'{msg}\' no es una opción válida.\nEscoge Verbos o Instrumentos Musicales",
"reply_markup": teclado
}
return ret
class EstadoValidaLetra:
def accion(self, msg, user_data):
resp = msg
resultado = ""
final = ""
if len(user_data.letras_palabra) > 0 and user_data.vidas > 0:
self.next = EstadoValidaLetra()
invalida = False
letra_user = resp.upper()
if letra_user in alfabeto - user_data.letras_usadas:
user_data.letras_usadas.add(letra_user)
if letra_user in user_data.letras_palabra:
user_data.letras_palabra.remove(letra_user)
else:
user_data.vidas -= 1
resultado = f'\n\'{letra_user}\' no es parte del nombre'
invalida = True
elif letra_user in user_data.letras_usadas:
resultado = '\nYa usaste esta letra, prueba con otra'
invalida = True
else:
resultado = '\nCaracter inválido'
invalida = True
if invalida:
self.next = EstadoValidaLetra()
final = "\nEnviame una letra"
if user_data.vidas == 0:
final = '\n\n--- Perdiste 😓️ \n--- la palabra era ' + user_data.palabra_juego.upper() + '\n (enviame un msg para continuar)'
self.next = EstadoFinal()
elif len(user_data.letras_palabra) == 0:
final = '\n\n--- 🎊️🎉️ Felicitaciones 🎊️🎉️ \n--- encontraste la palabra\n'+ '\n (enviame un msg para continuar)'
self.next = EstadoFinal()
lista_palabra = [letra if letra in user_data.letras_usadas else '_' for letra in user_data.palabra_juego]
if user_data.tipo_palabra == "instrumentos":
palabra_en_pantalla = '\nInstrumento Musical: ' + ' '.join(lista_palabra)
elif user_data.tipo_palabra == "verbos":
palabra_en_pantalla = '\nVerbo : ' + ' '.join(lista_palabra)
estatus = '\nIntentos restantes ['+ str(user_data.vidas) +']'+' - Letras usadas: '+''.join(user_data.letras_usadas)
info = resultado + anim[user_data.vidas] + estatus + palabra_en_pantalla + final
#botones = [[letra.upper()] for letra in alfabeto - user_data.letras_usadas]
teclado = ReplyKeyboardMarkup(BOTONES, one_time_keyboard=True)
ret = {
"msg": info,
"reply_markup": teclado
}
return ret
class EstadoFinal:
"""
Fin del juego
"""
def accion(self, msg, user_data):
teclado = ReplyKeyboardMarkup([["Jugar"]], one_time_keyboard=True)
self.next = EstadoSalto()
ret = {
"msg": "Quieres volver a jugar?",
"reply_markup": teclado
}
return ret
class EstadoSalto:
"""
Registra respuesta y salta a otro estado.
"""
def accion(self, msg, user_data):
if msg == "Jugar":
estado = EstadoSelec()
else:
estado = EstadoFinal()
ret = estado.accion(msg, user_data)
self.next = estado.next
return ret
def get_palabra(tipo_palabra):
""" Selecciona un string de la lista pasada como argumento """
if tipo_palabra == "verbos":
palabra = random.choice(verbos)
elif tipo_palabra == "instrumentos":
palabra = random.choice(instrumentos)
while '_' in palabra or ' ' in palabra:
palabra = random.choice(instrumentos)
tipo_palabra = "instrumentos"
return palabra.upper()

10852
02-colgado_bot/palabras.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
"""
Clase sessiones de usuario:
Nombre, Clase, Atributos
"""
from estados_colgado import EstadoSelec
class SesionUsuario():
def __init__(self):
self.user_data = UserData()
self.state = EstadoSelec()
def accion(self, msg):
ret = self.state.accion(msg, self.user_data)
self.state = self.state.next
return ret
class UserData:
def __init__(self):
self.id = ""
self.username = ""
self.nombre = ""
self.apellido = ""
self.es_bot = False
self.vidas = 0
self.tipo_palabra = ""
self.palabra_juego = ""
self.letras_palabra = set()
self.letras_usadas = set()

8
README.md Normal file
View File

@ -0,0 +1,8 @@
# Bots de Telegram con python
Libreria [python-telegram-bot](https://pypi.org/project/python-telegram-bot/)
Bots:
- 01 Bot básico con trabajos y comandos
- 02 Bot con juego el colgado, y otras funciones