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()
|
@ -6,16 +6,16 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
| Nombre | Descripción | Nivel |
|
| Nombre | Descripción | Nivel |
|
||||||
| ----------------------------------------- | ------------------------------------------------- | ---------- |
|
| ----------------------------------------------------- | ------------------------------------------------- | ---------- |
|
||||||
| [Bot id del chat](./01_id_bot/) | Bot que devuelve el id del bot | básico |
|
| [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 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 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 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 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](./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 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 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 recetas** (próximamente) | Bot que devuelve recetas de cocina | avanzado |
|
||||||
| **Bot de deportes** (próximamente) | Bot que devuelve información de deportes | avanzado |
|
| **Bot de deportes** (próximamente) | Bot que devuelve información de deportes | avanzado |
|
||||||
| **Bot de mareas** (próximamente) | Bot que devuelve información de mareas | avanzado |
|
| **Bot de mareas** (próximamente) | Bot que devuelve información de mareas | avanzado |
|
||||||
|
Loading…
Reference in New Issue
Block a user