Add movies trivial Bot for Telegram
This commit is contained in:
parent
be39d5b1d3
commit
ecd77967a0
25
catch-all/06_bots_telegram/07_movie2_bot/Dockerfile
Normal file
25
catch-all/06_bots_telegram/07_movie2_bot/Dockerfile
Normal file
@ -0,0 +1,25 @@
|
||||
# Utiliza una imagen base de Python en Alpine
|
||||
FROM python:3.12.4-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Instala bash, herramientas de compilación y librerías necesarias
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
python -m venv /env && \
|
||||
/env/bin/pip install --upgrade pip
|
||||
|
||||
# Añade el archivo de requisitos a la imagen
|
||||
COPY requirements.txt /app/requirements.txt
|
||||
|
||||
RUN /env/bin/pip install --no-cache-dir -r /app/requirements.txt
|
||||
|
||||
# Añade el resto del código de la aplicación
|
||||
COPY . /app
|
||||
|
||||
# Configura el entorno virtual
|
||||
ENV VIRTUAL_ENV=/env
|
||||
ENV PATH=/env/bin:$PATH
|
||||
|
||||
# Comando por defecto para ejecutar el bot
|
||||
CMD ["python", "main.py"]
|
67
catch-all/06_bots_telegram/07_movie2_bot/README.md
Normal file
67
catch-all/06_bots_telegram/07_movie2_bot/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
# QuizBot
|
||||
|
||||
__Basado en el repositorio de [CineMonster](https://github.com/RogueFairyStudios/CineMonster)__
|
||||
|
||||
Bot de Telegram con un juego basado en preguntas sobre películas. Resumen de las funcionalidades:
|
||||
|
||||
|
||||
## **Comandos Disponibles:**
|
||||
|
||||
1. **`/start`**:
|
||||
- **Descripción**: Inicia una nueva sesión de juego para el chat.
|
||||
- **Acciones**: Crea una nueva instancia de `Session` y la almacena en `SESSIONS`. Si se encuentra una clase `Quiz` en el módulo `quiz`, la inicializa para la sesión.
|
||||
|
||||
2. **`/roll`**:
|
||||
- **Descripción**: Lanza una pregunta de trivia sobre películas.
|
||||
- **Acciones**: Llama a `show` en el objeto `Quiz` de la sesión activa. Envía una imagen de una película al chat y establece el estado del juego en "running".
|
||||
|
||||
3. **`/leaderboard`**:
|
||||
- **Descripción**: Muestra la tabla de clasificación de los jugadores.
|
||||
- **Acciones**: Envía la tabla de clasificación actual a través del `messenger`. La tabla muestra los jugadores y sus puntos.
|
||||
|
||||
4. **`/repeat`**:
|
||||
- **Descripción**: Repite la última pregunta de trivia sobre películas.
|
||||
- **Acciones**: Envía de nuevo la imagen de la película al chat junto con la pregunta sobre el título de la película.
|
||||
|
||||
5. **`/cut`**:
|
||||
- **Descripción**: Permite que un jugador abandone el juego.
|
||||
- **Acciones**: Elimina al jugador de la sesión actual y notifica al chat que el jugador ha abandonado el juego.
|
||||
|
||||
6. **`/stop`**:
|
||||
- **Descripción**: Finaliza la sesión de juego actual.
|
||||
- **Acciones**: Elimina la sesión del chat actual de `SESSIONS` y notifica al chat que el juego ha terminado.
|
||||
|
||||
7. **`/check_resps`**:
|
||||
- **Descripción**: Verifica las respuestas enviadas por los jugadores.
|
||||
- **Acciones**: Compara la respuesta del usuario con la respuesta correcta de la película y actualiza el puntaje si la respuesta es correcta.
|
||||
|
||||
|
||||
## **Funcionalidades Adicionales:**
|
||||
|
||||
- **Manejo de Temporizadores**:
|
||||
- Utiliza `apscheduler` para ejecutar `update_all_timers` cada minuto, lo que actualiza los temporizadores de todas las sesiones y verifica la expiración del tiempo de juego.
|
||||
|
||||
- **Mensajería**:
|
||||
- Usa un objeto `messenger` para enviar mensajes y fotos a los usuarios en el chat, manejando la comunicación con Telegram.
|
||||
|
||||
- **Gestión de Jugadores**:
|
||||
- Permite agregar y quitar jugadores de la sesión. Actualiza el puntaje de los jugadores en función de sus respuestas correctas.
|
||||
|
||||
- **Control de Estado del Juego**:
|
||||
- Los estados del juego (`running`, `stopped`, `timed_out`) controlan el flujo del juego, incluyendo la verificación de respuestas y el manejo de tiempos de espera.
|
||||
|
||||
- **Manejo de Errores**:
|
||||
- Maneja errores durante el proceso de actualización y respuesta, notificando a los usuarios en caso de problemas con la pregunta de trivia o el estado de la sesión.
|
||||
|
||||
|
||||
## **Estructura del Código:**
|
||||
|
||||
1. **`Session`**:
|
||||
- Maneja la lógica del juego, incluidos los jugadores, el estado de la sesión, y los temporizadores.
|
||||
|
||||
2. **`Quiz`**:
|
||||
- Se encarga de la lógica relacionada con las preguntas sobre películas, incluida la selección de una película al azar y la verificación de las respuestas.
|
||||
|
||||
3. **`Server`**:
|
||||
- Configura el bot de Telegram, maneja los comandos y los eventos, y gestiona las sesiones de juego.
|
||||
|
1
catch-all/06_bots_telegram/07_movie2_bot/_config.yml
Normal file
1
catch-all/06_bots_telegram/07_movie2_bot/_config.yml
Normal file
@ -0,0 +1 @@
|
||||
theme: jekyll-theme-slate
|
@ -0,0 +1,40 @@
|
||||
from miners import Miner
|
||||
from random import *
|
||||
|
||||
|
||||
class Collection:
|
||||
|
||||
movie_list = ''
|
||||
|
||||
def __init__(self, miner, type):
|
||||
self.miner = miner
|
||||
self.type = type
|
||||
|
||||
def top_250(self):
|
||||
self.movie_list = self.miner.get_top(250)
|
||||
|
||||
def general(self):
|
||||
pass
|
||||
|
||||
def get_rand_movie(self):
|
||||
movie = None
|
||||
|
||||
while movie is None:
|
||||
if self.type is None:
|
||||
number = str(randrange(1, 99999))
|
||||
if len(number) < 7:
|
||||
number = '0' * (7 - len(number)) + number
|
||||
movie_id = 'tt' + number
|
||||
else:
|
||||
self.top_250()
|
||||
number = randrange(0, len(self.movie_list) - 1)
|
||||
movie_id = self.movie_list[number]['tconst']
|
||||
|
||||
images, movie = self.miner.get_movie_by_id(movie_id)
|
||||
print(movie['base']['title'])
|
||||
if images is not None:
|
||||
if images['totalImageCount'] < 1:
|
||||
movie = None
|
||||
|
||||
return movie, images
|
||||
|
@ -0,0 +1 @@
|
||||
from collection.Collection import Collection
|
@ -0,0 +1 @@
|
||||
from conf.config import Config, ProductionConfig, DevelopmentConfig, TestingConfig
|
31
catch-all/06_bots_telegram/07_movie2_bot/conf/config.py
Normal file
31
catch-all/06_bots_telegram/07_movie2_bot/conf/config.py
Normal file
@ -0,0 +1,31 @@
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
load_dotenv('.env')
|
||||
|
||||
|
||||
class Config(object):
|
||||
|
||||
DEBUG = False
|
||||
TESTING = False
|
||||
DATABASE_URI = 'sqlite://:memory:'
|
||||
DATABASE_URL = os.getenv('DATABASE_URL')
|
||||
TELEGRAM_BOT_API = os.getenv('TELEGRAM_BOT_API')
|
||||
LOG_FILE = 'logs/movie2_bot.log'
|
||||
QUIZ_LANG = 'es'
|
||||
|
||||
|
||||
class ProductionConfig(Config):
|
||||
DATABASE_URI = 'mysql://user@localhost/foo'
|
||||
SESSION_EXPIRATION_TIME = 30
|
||||
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
DEBUG = True
|
||||
SESSION_EXPIRATION_TIME = 10
|
||||
|
||||
|
||||
class TestingConfig(Config):
|
||||
TESTING = True
|
20
catch-all/06_bots_telegram/07_movie2_bot/docker-compose.yaml
Normal file
20
catch-all/06_bots_telegram/07_movie2_bot/docker-compose.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
# version: '3.7'
|
||||
|
||||
services:
|
||||
movie2_bot:
|
||||
env_file:
|
||||
- .env
|
||||
image: movie2_bot_python:latest
|
||||
container_name: movie2_bot
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Madrid
|
||||
volumes:
|
||||
- ./movie2_bot_data:/app/db
|
||||
- ./logs:/app/logs
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
movie2_bot_data:
|
||||
logs:
|
@ -0,0 +1 @@
|
||||
from interfaces.telegram.messenger import Messenger as TelegramMessenger
|
@ -0,0 +1,45 @@
|
||||
from telegram.constants import ParseMode
|
||||
import telegram.ext
|
||||
|
||||
class Messenger:
|
||||
formats = {
|
||||
'regular': "*%s*",
|
||||
'caption': "*%s*",
|
||||
'title': "=+= *%s* =+=",
|
||||
'highlight': "--+ %s +--",
|
||||
'bold': "*%s* %s"
|
||||
}
|
||||
|
||||
def __init__(self, bot, logger):
|
||||
self.logger = logger
|
||||
self.logger.debug("Started...")
|
||||
self.bot = bot
|
||||
|
||||
def send_msg(self, chat_id, msg, type_msg='regular'):
|
||||
if type_msg not in self.formats:
|
||||
self.logger.error(f"Invalid message type: {type_msg}")
|
||||
return
|
||||
|
||||
formatted_msg = self.format(type_msg, msg)
|
||||
try:
|
||||
self.bot.send_message(
|
||||
chat_id=chat_id,
|
||||
text=formatted_msg,
|
||||
parse_mode=ParseMode.MARKDOWN_V2
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error sending message: {e}")
|
||||
|
||||
def format(self, type_msg, msg):
|
||||
return self.formats[type_msg] % msg
|
||||
|
||||
def send_photo(self, chat_id, photo, caption):
|
||||
try:
|
||||
self.bot.send_photo(
|
||||
chat_id=chat_id,
|
||||
photo=photo,
|
||||
caption=caption,
|
||||
parse_mode=ParseMode.MARKDOWN_V2
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error sending photo: {e}")
|
11
catch-all/06_bots_telegram/07_movie2_bot/main.py
Normal file
11
catch-all/06_bots_telegram/07_movie2_bot/main.py
Normal file
@ -0,0 +1,11 @@
|
||||
from server import Server
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
Server()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
main()
|
20
catch-all/06_bots_telegram/07_movie2_bot/miners/Miner.py
Normal file
20
catch-all/06_bots_telegram/07_movie2_bot/miners/Miner.py
Normal file
@ -0,0 +1,20 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class Miner(object):
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
handle = None
|
||||
|
||||
@abstractmethod
|
||||
def top_list(self, number):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_movie_id(self, index):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_movie_by_id(self, movie_id):
|
||||
pass
|
@ -0,0 +1,2 @@
|
||||
from miners.Miner import Miner
|
||||
from miners.imdb.ImdbMiner import IMDB
|
@ -0,0 +1,20 @@
|
||||
from miners.Miner import Miner
|
||||
from imdbpie import Imdb
|
||||
|
||||
|
||||
class IMDB(Miner):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.handle = Imdb()
|
||||
super(IMDB, self).__init__()
|
||||
|
||||
def top_list(self, number):
|
||||
pop_movies = self.handle.top_250()
|
||||
return pop_movies
|
||||
|
||||
def get_movie_id(self, index):
|
||||
return "tt" + index
|
||||
|
||||
def get_movie_by_id(self, movie_id):
|
||||
return self.handle.get_title_images(movie_id), self.handle.get_title(movie_id)
|
@ -0,0 +1 @@
|
||||
from miners.imdb.ImdbMiner import IMDB
|
44
catch-all/06_bots_telegram/07_movie2_bot/models/Model.py
Normal file
44
catch-all/06_bots_telegram/07_movie2_bot/models/Model.py
Normal file
@ -0,0 +1,44 @@
|
||||
import os
|
||||
import sys
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
from conf import Config
|
||||
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class Player(Base):
|
||||
__tablename__ = 'player'
|
||||
id = Column(Integer, primary_key=True)
|
||||
telegram_uid = Column(String(250), nullable=True)
|
||||
email = Column(String(250), nullable=True)
|
||||
phone = Column(String(30), nullable=True)
|
||||
|
||||
|
||||
class Group(Base):
|
||||
__tablename__ = 'group'
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(250), primary_key=True)
|
||||
player_id = Column(Integer, ForeignKey('player.id'))
|
||||
points = Column(Integer, nullable=True)
|
||||
player = relationship(Player)
|
||||
|
||||
|
||||
class Session(Base):
|
||||
__tablename__ = 'session'
|
||||
id = Column(Integer, primary_key=True)
|
||||
group_id = Column(Integer, ForeignKey('group.id'))
|
||||
group = relationship(Group)
|
||||
started = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
ended = Column(DateTime)
|
||||
|
||||
|
||||
engine = create_engine(Config.DATABASE_URL)
|
||||
|
||||
Base.metadata.create_all(engine)
|
@ -0,0 +1 @@
|
||||
from models.Model import Player, Group, Session, engine
|
16
catch-all/06_bots_telegram/07_movie2_bot/movie/Movie.py
Normal file
16
catch-all/06_bots_telegram/07_movie2_bot/movie/Movie.py
Normal file
@ -0,0 +1,16 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class Movie(ABC):
|
||||
|
||||
id = 0
|
||||
name = ''
|
||||
type = ''
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.type = None
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
8
catch-all/06_bots_telegram/07_movie2_bot/movie/Pop.py
Normal file
8
catch-all/06_bots_telegram/07_movie2_bot/movie/Pop.py
Normal file
@ -0,0 +1,8 @@
|
||||
from movie.Movie import Movie
|
||||
|
||||
|
||||
class Pop(Movie):
|
||||
|
||||
def __init__(self):
|
||||
super(Pop, self).__init__(name='Pop Movie')
|
||||
self.type = "pop"
|
@ -0,0 +1,2 @@
|
||||
from movie.Movie import Movie
|
||||
from movie.Pop import Pop
|
17
catch-all/06_bots_telegram/07_movie2_bot/player/Player.py
Normal file
17
catch-all/06_bots_telegram/07_movie2_bot/player/Player.py
Normal file
@ -0,0 +1,17 @@
|
||||
class Player:
|
||||
|
||||
id = ''
|
||||
points = 0
|
||||
name = ''
|
||||
|
||||
def __init__(self, uid):
|
||||
self.id = uid
|
||||
|
||||
def get_points(self):
|
||||
return self.points
|
||||
|
||||
def set_name(self, name):
|
||||
self.name = name
|
||||
|
||||
def add_points(self, points):
|
||||
self.points += points
|
@ -0,0 +1 @@
|
||||
from player.Player import Player
|
98
catch-all/06_bots_telegram/07_movie2_bot/quiz/Quiz.py
Normal file
98
catch-all/06_bots_telegram/07_movie2_bot/quiz/Quiz.py
Normal file
@ -0,0 +1,98 @@
|
||||
from player.Player import Player
|
||||
from random import choice
|
||||
from collection.Collection import Collection
|
||||
from miners.imdb.ImdbMiner import IMDB
|
||||
|
||||
|
||||
class Quiz:
|
||||
movies_type = ''
|
||||
movie = None
|
||||
images = None
|
||||
|
||||
def __init__(self, session):
|
||||
self.miner = IMDB()
|
||||
self.session = session
|
||||
|
||||
def set_level(self, level):
|
||||
# Implementar el ajuste del nivel si es necesario
|
||||
pass
|
||||
|
||||
def rand_movie(self, rand_type=None):
|
||||
collection = Collection(self.miner, rand_type)
|
||||
self.movie, self.images = collection.get_rand_movie()
|
||||
|
||||
def get_movie_photo(self):
|
||||
if not self.images or 'images' not in self.images:
|
||||
raise ValueError("No images available")
|
||||
try:
|
||||
return choice(self.images['images'])['url']
|
||||
except (IndexError, KeyError) as e:
|
||||
raise ValueError("Error selecting image URL") from e
|
||||
|
||||
def get_question(self, rand_type=None):
|
||||
try:
|
||||
self.rand_movie(rand_type)
|
||||
return self.get_movie_photo()
|
||||
except ValueError:
|
||||
return _("not_possible_find_movie")
|
||||
|
||||
def show(self, update, rand_type):
|
||||
chat_id = update.message.chat_id
|
||||
try:
|
||||
movie_img = self.get_question(rand_type)
|
||||
self.session.messenger.send_msg(chat_id, "movie_bot", "title")
|
||||
self.session.messenger.send_photo(
|
||||
chat_id, movie_img, caption=_("question_which_movie")
|
||||
)
|
||||
self.session.updater_counter()
|
||||
self.session.status = "running"
|
||||
except ValueError as e:
|
||||
self.session.messenger.send_msg(
|
||||
chat_id,
|
||||
msg=_("error_fetching_question"),
|
||||
type_msg="bold"
|
||||
)
|
||||
self.session.status = "stopped"
|
||||
|
||||
def check_resps(self, update):
|
||||
chat_id = update.message.chat_id
|
||||
if not self.movie or 'base' not in self.movie or 'title' not in self.movie['base']:
|
||||
self.session.messenger.send_msg(
|
||||
chat_id,
|
||||
msg=_("error_movie_data"),
|
||||
type_msg="bold"
|
||||
)
|
||||
return
|
||||
|
||||
if str.lower(self.movie['base']['title']) == str.lower(update.message.text):
|
||||
player = Player(update.message.from_user.id)
|
||||
player.name = f"{update.message.from_user.first_name} {update.message.from_user.last_name}"
|
||||
try:
|
||||
self.session.player_add(player)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.session.players[update.message.from_user.id].add_points(1)
|
||||
self.session.messenger.send_msg(
|
||||
chat_id,
|
||||
msg=(player.name, _("correct_answer")),
|
||||
type_msg="bold"
|
||||
)
|
||||
self.movie = None
|
||||
self.session.status = "stopped"
|
||||
|
||||
def check_expiration(self):
|
||||
try:
|
||||
self.session.update_timer()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if self.session.status == "timed_out":
|
||||
self.session.messenger.send_msg(
|
||||
chat_id=self.session.chat_id,
|
||||
msg=_("times_up", self.movie['base']
|
||||
['title'] if self.movie else ""),
|
||||
type_msg="bold"
|
||||
)
|
||||
self.session.status = "stopped"
|
||||
self.movie = None
|
@ -0,0 +1 @@
|
||||
from quiz.Quiz import Quiz
|
@ -0,0 +1,6 @@
|
||||
Babel==2.15.0
|
||||
boto3==1.34.145
|
||||
imdbpie~=4.0
|
||||
python-dotenv==1.0.1
|
||||
python-telegram-bot[job-queue]==21.4
|
||||
six==1.16.0
|
1
catch-all/06_bots_telegram/07_movie2_bot/runtime.txt
Normal file
1
catch-all/06_bots_telegram/07_movie2_bot/runtime.txt
Normal file
@ -0,0 +1 @@
|
||||
python-3.12.4
|
225
catch-all/06_bots_telegram/07_movie2_bot/server/Server.py
Normal file
225
catch-all/06_bots_telegram/07_movie2_bot/server/Server.py
Normal file
@ -0,0 +1,225 @@
|
||||
import logging
|
||||
from argparse import ArgumentParser
|
||||
from telegram import Update
|
||||
from telegram.constants import ParseMode
|
||||
from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
|
||||
|
||||
import conf
|
||||
import interfaces
|
||||
import player
|
||||
import quiz
|
||||
import session
|
||||
from translations.required import *
|
||||
|
||||
|
||||
class Server:
|
||||
logger = logging.getLogger(__name__)
|
||||
SESSIONS = dict()
|
||||
|
||||
def __init__(self):
|
||||
self.config_instance = self.config_init()
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.DEBUG,
|
||||
handlers=[
|
||||
logging.FileHandler(self.config_instance.LOG_FILE),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
self.application = Application.builder().token(
|
||||
self.config_instance.TELEGRAM_BOT_API).build()
|
||||
|
||||
# Add handlers
|
||||
self.application.add_handler(MessageHandler(
|
||||
filters.TEXT & ~filters.COMMAND, self.command_check_resps)) # Add handler for non-command messages
|
||||
self.application.add_handler(
|
||||
CommandHandler('start', self.command_start))
|
||||
self.application.add_handler(CommandHandler('roll', self.command_roll))
|
||||
self.application.add_handler(CommandHandler(
|
||||
"leaderboard", self.command_leaderboard))
|
||||
self.application.add_handler(
|
||||
CommandHandler("repeat", self.command_repeat))
|
||||
self.application.add_handler(CommandHandler("cut", self.command_cut))
|
||||
self.application.add_handler(CommandHandler("stop", self.command_stop))
|
||||
self.application.add_error_handler(self.error)
|
||||
|
||||
# Set up job queue
|
||||
self.application.job_queue.run_repeating(
|
||||
self.update_all_timers, interval=60)
|
||||
|
||||
self.logger.info("Iniciando...")
|
||||
|
||||
# Start the Bot
|
||||
self.application.run_polling()
|
||||
|
||||
def config_init(self):
|
||||
self.logger.info("Config init...")
|
||||
|
||||
arg_parser = ArgumentParser(description="Movie2 Bot")
|
||||
|
||||
arg_parser.add_argument(
|
||||
"-e", "--env", metavar="env", type=str, default="prod",
|
||||
help="Environment to run the bot: dev, test or prod"
|
||||
)
|
||||
|
||||
arg_parser.add_argument(
|
||||
"-v", "--verbose", metavar="verbose", type=bool, default=False,
|
||||
help="Print information about running bot"
|
||||
)
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
if args.env == "prod":
|
||||
return conf.ProductionConfig()
|
||||
elif args.env == "dev":
|
||||
return conf.DevelopmentConfig()
|
||||
else:
|
||||
return conf.TestingConfig()
|
||||
|
||||
async def error(self, update: Update, context: CallbackContext):
|
||||
self.logger.warning(
|
||||
f'Update "{update}" caused error "{context.error}"')
|
||||
|
||||
async def update_all_timers(self, context: CallbackContext):
|
||||
self.logger.info("Updating all timers...")
|
||||
for sess in self.SESSIONS.values():
|
||||
sess.update_timers()
|
||||
if hasattr(sess, 'quiz'):
|
||||
sess.quiz.check_expiration()
|
||||
else:
|
||||
self.logger.error("Quiz class not found in session")
|
||||
|
||||
async def command_start(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Command start...")
|
||||
chat_id = update.message.chat_id
|
||||
await update.message.reply_text('¡Hola! El comando /start ha sido recibido.')
|
||||
|
||||
if chat_id not in self.SESSIONS:
|
||||
self.messenger = interfaces.TelegramMessenger(
|
||||
context.bot, self.logger
|
||||
)
|
||||
self.SESSIONS[chat_id] = session.Session(
|
||||
chat_id, self.config_instance, self.logger
|
||||
)
|
||||
self.SESSIONS[chat_id].set_messenger(self.messenger)
|
||||
|
||||
# Crear una instancia de la clase Quiz
|
||||
try:
|
||||
self.SESSIONS[chat_id].quiz = quiz.Quiz(self.SESSIONS[chat_id])
|
||||
except AttributeError as e:
|
||||
self.logger.error(f"Error creating Quiz instance: {e}")
|
||||
await self.messenger.send_msg(
|
||||
chat_id, _("error_creating_quiz_instance")
|
||||
)
|
||||
logging.error(f"Error creating Quiz instance: {e}")
|
||||
|
||||
async def command_roll(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Command roll...")
|
||||
chat_id = update.message.chat_id
|
||||
args = context.args
|
||||
rand_type = args[0] if args else None
|
||||
await self.SESSIONS[chat_id].messenger.send_msg(
|
||||
chat_id, _("searching_movies"))
|
||||
if hasattr(self.SESSIONS[chat_id], 'quiz'):
|
||||
await self.SESSIONS[chat_id].quiz.show(update, rand_type)
|
||||
else:
|
||||
self.logger.error("Quiz instance not found in session")
|
||||
|
||||
async def command_leaderboard(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Command leaderboard...")
|
||||
chat_id = update.message.chat_id
|
||||
sess = self.SESSIONS.get(chat_id)
|
||||
if sess:
|
||||
try:
|
||||
await sess.messenger.send_msg(
|
||||
chat_id, _("leader_board_title"), 'highlights'
|
||||
)
|
||||
ldb = sess.get_leaderboard()
|
||||
await sess.messenger.send_msg(chat_id, ldb)
|
||||
except ValueError as e:
|
||||
await sess.messenger.send_msg(
|
||||
chat_id, f'{update.message.from_user.first_name} {e.args[0]}'
|
||||
)
|
||||
else:
|
||||
await update.message.reply_text(_("session_not_found"))
|
||||
|
||||
async def command_action(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Command action...")
|
||||
chat_id = update.message.chat_id
|
||||
try:
|
||||
player = player.Player(update.message.from_user.id)
|
||||
player.name = f'{update.message.from_user.first_name} {update.message.from_user.last_name}'
|
||||
self.SESSIONS[chat_id].player_add(player)
|
||||
await self.SESSIONS[chat_id].messenger.send(
|
||||
update, f'{player.name} {_("joined_the_game")}'
|
||||
)
|
||||
except ValueError as e:
|
||||
await self.SESSIONS[chat_id].messenger.send(
|
||||
update, f'{update.message.from_user.first_name} {e.args[0]}'
|
||||
)
|
||||
|
||||
async def command_repeat(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Command repeat...")
|
||||
chat_id = update.message.chat_id
|
||||
if chat_id in self.SESSIONS:
|
||||
movie_img = self.SESSIONS[chat_id].quiz.get_question()
|
||||
await self.SESSIONS[chat_id].messenger.send_msg(
|
||||
chat_id, _("repeting")
|
||||
)
|
||||
await self.SESSIONS[chat_id].messenger.send_photo(
|
||||
chat_id=chat_id, photo=movie_img,
|
||||
caption=_("what_is_the_movie_series_name")
|
||||
)
|
||||
await self.SESSIONS[chat_id].messenger.send_msg(
|
||||
chat_id, "==========================="
|
||||
)
|
||||
else:
|
||||
await update.message.reply_text(_("session_not_found"))
|
||||
|
||||
async def command_cut(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Command cut...")
|
||||
chat_id = update.message.chat_id
|
||||
if chat_id in self.SESSIONS:
|
||||
try:
|
||||
player = player.Player(update.message.from_user.id)
|
||||
self.SESSIONS[chat_id].player_quit(player)
|
||||
await self.SESSIONS[chat_id].messenger.send(
|
||||
update, f'{player.name} {_("quit_the_game")}'
|
||||
)
|
||||
except ValueError as e:
|
||||
await self.SESSIONS[chat_id].messenger.send(
|
||||
update, f'{update.message.from_user.first_name} {e.args[0]}'
|
||||
)
|
||||
else:
|
||||
await update.message.reply_text(_("session_not_found"))
|
||||
|
||||
async def command_stop(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Command stop...")
|
||||
chat_id = update.message.chat_id
|
||||
if chat_id in self.SESSIONS:
|
||||
self.SESSIONS[chat_id].stop()
|
||||
await self.SESSIONS[chat_id].messenger.send(
|
||||
update, _("game_stopped")
|
||||
)
|
||||
else:
|
||||
await update.message.reply_text(_("session_not_found"))
|
||||
|
||||
# Implement the command_check_resps method
|
||||
async def command_check_resps(self, update: Update, context: CallbackContext):
|
||||
self.logger.info("Checking responses...")
|
||||
chat_id = update.message.chat_id
|
||||
if chat_id in self.SESSIONS:
|
||||
user_response = update.message.text
|
||||
sess = self.SESSIONS[chat_id]
|
||||
# Add logic to handle user responses here
|
||||
await sess.messenger.send_msg(chat_id, f"Received: {user_response}")
|
||||
else:
|
||||
await update.message.reply_text(_("session_not_found"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Server()
|
@ -0,0 +1 @@
|
||||
from server.Server import Server
|
55
catch-all/06_bots_telegram/07_movie2_bot/session/Session.py
Normal file
55
catch-all/06_bots_telegram/07_movie2_bot/session/Session.py
Normal file
@ -0,0 +1,55 @@
|
||||
import datetime
|
||||
|
||||
|
||||
class Session:
|
||||
|
||||
def __init__(self, chat_id, config, logger):
|
||||
self.logger = logger
|
||||
self.started = datetime.datetime.utcnow()
|
||||
self.chat_id = chat_id
|
||||
self.config = config
|
||||
self.expiration = self.config.SESSION_EXPIRATION_TIME
|
||||
self.players = {}
|
||||
self.status = ''
|
||||
self.messenger = None
|
||||
self.counter = datetime.datetime.utcnow()
|
||||
|
||||
def player_add(self, player):
|
||||
if player.id not in self.players:
|
||||
self.players[player.id] = player
|
||||
else:
|
||||
self.update_log()
|
||||
raise ValueError('Ya está en la partida...')
|
||||
|
||||
def player_quit(self, player):
|
||||
if player.id in self.players:
|
||||
del self.players[player.id]
|
||||
else:
|
||||
raise ValueError('no_está_en_la_partida')
|
||||
|
||||
def end(self):
|
||||
self.ended = datetime.datetime.utcnow()
|
||||
|
||||
def get_leaderboard(self):
|
||||
ldb = ''
|
||||
for player in self.players.values():
|
||||
ldb += f'{player.name} : {player.get_points()}\n'
|
||||
return ldb
|
||||
|
||||
def set_messenger(self, messenger):
|
||||
self.messenger = messenger
|
||||
|
||||
def update_timers(self):
|
||||
if self.status == 'running':
|
||||
elapsed = self.update_log()
|
||||
if elapsed.seconds > self.expiration:
|
||||
self.status = 'timed_out'
|
||||
|
||||
def update_counter(self):
|
||||
self.counter = datetime.datetime.utcnow()
|
||||
self.logger.debug(f'{self.chat_id} : updater_counter: {self.counter}')
|
||||
|
||||
def update_log(self):
|
||||
elapsed = datetime.datetime.utcnow() - self.counter
|
||||
self.logger.debug(f'{self.chat_id} : updater_timer: {elapsed}')
|
||||
return elapsed
|
@ -0,0 +1 @@
|
||||
from session.Session import Session
|
@ -0,0 +1,2 @@
|
||||
[run]
|
||||
omit = */.env/*
|
@ -0,0 +1 @@
|
||||
[python: **.py]
|
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script for Generate Translation files
|
||||
# @author: manuelver
|
||||
# requires: pybabel
|
||||
|
||||
TRANSLATION_DIR=./
|
||||
LANGS=("pt_BR" "en" "es" "ca") # todo: smarter detection
|
||||
GENERATED_DIR=./generated
|
||||
options=("create" "update" "compile" "clean" "quit")
|
||||
select opt in "${options[@]}"; do
|
||||
case $opt in
|
||||
"create")
|
||||
echo "Creating files"
|
||||
pybabel extract -F babel.cfg -o cinemonster.pot ../
|
||||
for lang in ${LANGS[@]}; do
|
||||
pybabel init -i cinemonster.pot -d "${GENERATED_DIR}" -l ${lang}
|
||||
done
|
||||
;;
|
||||
"update")
|
||||
echo "updating files"
|
||||
pybabel update -i cinemonster.pot --previous -d "${GENERATED_DIR}"
|
||||
;;
|
||||
"compile")
|
||||
echo "compiling files"
|
||||
pybabel compile -d "${GENERATED_DIR}"
|
||||
;;
|
||||
"clean")
|
||||
echo "cleaning files"
|
||||
rm -rf "${GENERATED_DIR}"
|
||||
rm cinemonster.pot
|
||||
;;
|
||||
"quit")
|
||||
break
|
||||
;;
|
||||
*) echo invalid option ;;
|
||||
esac
|
||||
done
|
@ -0,0 +1,68 @@
|
||||
# Catalan translations for PROJECT.
|
||||
# Copyright (C) 2024 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-07-18 02:00+0200\n"
|
||||
"PO-Revision-Date: 2024-07-18 02:00+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: ca\n"
|
||||
"Language-Team: ca <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: ../quiz/Quiz.py:35
|
||||
msgid "not_possible_find_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:42
|
||||
msgid "question_which_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:61
|
||||
msgid "correct_answer"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:76
|
||||
msgid "times_up"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:89
|
||||
msgid "searching_movies"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:97
|
||||
msgid "leader_board_title"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:114
|
||||
msgid " joined_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:124
|
||||
msgid "repeting"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:128
|
||||
msgid "what_is_the_movie_series_name"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:142
|
||||
msgid " left_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:153
|
||||
msgid "ending_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:157
|
||||
msgid "game_was_not_finished"
|
||||
msgstr ""
|
||||
|
@ -0,0 +1,68 @@
|
||||
# English translations for PROJECT.
|
||||
# Copyright (C) 2024 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-07-18 02:00+0200\n"
|
||||
"PO-Revision-Date: 2024-07-18 02:00+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: en\n"
|
||||
"Language-Team: en <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: ../quiz/Quiz.py:35
|
||||
msgid "not_possible_find_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:42
|
||||
msgid "question_which_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:61
|
||||
msgid "correct_answer"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:76
|
||||
msgid "times_up"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:89
|
||||
msgid "searching_movies"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:97
|
||||
msgid "leader_board_title"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:114
|
||||
msgid " joined_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:124
|
||||
msgid "repeting"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:128
|
||||
msgid "what_is_the_movie_series_name"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:142
|
||||
msgid " left_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:153
|
||||
msgid "ending_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:157
|
||||
msgid "game_was_not_finished"
|
||||
msgstr ""
|
||||
|
@ -0,0 +1,68 @@
|
||||
# Spanish translations for PROJECT.
|
||||
# Copyright (C) 2024 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-07-18 02:00+0200\n"
|
||||
"PO-Revision-Date: 2024-07-18 02:00+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: es\n"
|
||||
"Language-Team: es <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: ../quiz/Quiz.py:35
|
||||
msgid "not_possible_find_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:42
|
||||
msgid "question_which_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:61
|
||||
msgid "correct_answer"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:76
|
||||
msgid "times_up"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:89
|
||||
msgid "searching_movies"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:97
|
||||
msgid "leader_board_title"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:114
|
||||
msgid " joined_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:124
|
||||
msgid "repeting"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:128
|
||||
msgid "what_is_the_movie_series_name"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:142
|
||||
msgid " left_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:153
|
||||
msgid "ending_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:157
|
||||
msgid "game_was_not_finished"
|
||||
msgstr ""
|
||||
|
@ -0,0 +1,68 @@
|
||||
# Portuguese (Brazil) translations for PROJECT.
|
||||
# Copyright (C) 2024 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-07-18 02:00+0200\n"
|
||||
"PO-Revision-Date: 2024-07-18 02:00+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: pt_BR\n"
|
||||
"Language-Team: pt_BR <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: ../quiz/Quiz.py:35
|
||||
msgid "not_possible_find_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:42
|
||||
msgid "question_which_movie"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:61
|
||||
msgid "correct_answer"
|
||||
msgstr ""
|
||||
|
||||
#: ../quiz/Quiz.py:76
|
||||
msgid "times_up"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:89
|
||||
msgid "searching_movies"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:97
|
||||
msgid "leader_board_title"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:114
|
||||
msgid " joined_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:124
|
||||
msgid "repeting"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:128
|
||||
msgid "what_is_the_movie_series_name"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:142
|
||||
msgid " left_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:153
|
||||
msgid "ending_the_game"
|
||||
msgstr ""
|
||||
|
||||
#: ../server/Server.py:157
|
||||
msgid "game_was_not_finished"
|
||||
msgstr ""
|
||||
|
@ -0,0 +1,20 @@
|
||||
import gettext
|
||||
|
||||
""" localization support """
|
||||
en = gettext.translation(
|
||||
'messages', 'translations/generated', languages=['en']
|
||||
)
|
||||
|
||||
pt_br = gettext.translation(
|
||||
'messages', 'translations/generated', languages=['pt_BR']
|
||||
)
|
||||
|
||||
es = gettext.translation(
|
||||
'messages', 'translations/generated', languages=['es']
|
||||
)
|
||||
|
||||
ca = gettext.translation(
|
||||
'messages', 'translations/generated', languages=['ca']
|
||||
)
|
||||
|
||||
en.install()
|
@ -7,14 +7,14 @@
|
||||
</div>
|
||||
|
||||
| Nombre | Descripción | Nivel |
|
||||
| ----------------------------------------- | ------------------------------------------------- | ---------- |
|
||||
| ----------------------------------------------------- | ------------------------------------------------- | ---------- |
|
||||
| [Bot id del chat](./01_id_bot/) | Bot que devuelve el id del bot | básico |
|
||||
| [Bot mensajes](./02_pruebas_bot/) | Bot que devuelve mensajes básicos. Loggin | básico |
|
||||
| [Bot de traducción](./03_translator_bot/) | Bot que traduce mensajes a varios idiomas. Loggin | intermedio |
|
||||
| [Bot de clima](./04_clima_bot/) | Bot que devuelve el clima de una ciudad | intermedio |
|
||||
| [Bot de noticias](./05_rss_bot/) | Bot que devuelve noticias de última hora | intermedio |
|
||||
| [Bot de películas I](./06_movie_bot/) | Bot que devuelve información de películas | intermedio |
|
||||
| **Bot de películas II** (próximamente) | Bot que devuelve información de series | avanzado |
|
||||
| [Bot de películas](./06_movie_bot/) | Bot que devuelve información de películas | intermedio |
|
||||
| [Bot trivial de películas](./07_movie2_bot/README.md) | Bot que devuelve información de series | avanzado |
|
||||
| **Bot de libros** (próximamente) | Bot que devuelve información de libros | avanzado |
|
||||
| **Bot de recetas** (próximamente) | Bot que devuelve recetas de cocina | avanzado |
|
||||
| **Bot de deportes** (próximamente) | Bot que devuelve información de deportes | avanzado |
|
||||
|
Loading…
Reference in New Issue
Block a user