From 662342863e37e8661f818218df7bb72bc18bf4eb Mon Sep 17 00:00:00 2001 From: Mikhail Kirillov Date: Wed, 9 Oct 2024 04:53:26 +0400 Subject: Add movies commands --- commands/__init__.py | 4 +- commands/movie.py | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++ commands/users.py | 36 +++++++++++++++--- main.py | 13 ++++++- requirements.txt | 1 + strings.py | 18 +++++++++ utils.py | 15 +++++++- 7 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 commands/movie.py diff --git a/commands/__init__.py b/commands/__init__.py index b494867..09c0867 100644 --- a/commands/__init__.py +++ b/commands/__init__.py @@ -10,4 +10,6 @@ # from .meta import about -from .users import set_users, add_users, list_users, remove_users, chooser_user +from .users import set_users, add_users, list_users, remove_users, \ + chooser_user, who_is_next +from .movie import movie, movies, remove_movies diff --git a/commands/movie.py b/commands/movie.py new file mode 100644 index 0000000..b7be8cd --- /dev/null +++ b/commands/movie.py @@ -0,0 +1,105 @@ +# This file is part of python-cinema-club-bot +# contributed in 2024 by Mikhail Kirillov (~w96k) + +# To the extent possible under law, the author(s) have dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty. + +# You should have received a copy of the CC0 Public Domain Dedication along +# with this software. If not, see: +# + +from telegram import Update, error +from telegram.ext import ContextTypes +from imdb import Cinemagoer +from pprint import pformat + +from utils import context_init + +from strings import MOVIE_NOT_PROVIDED, EXPECTED_ONE_MOVIE, \ + MOVIE_ANOTHER_USER, FETCHING_MOVIE, FETCHING_ERROR, \ + MOVIE_REMOVE, MOVIE_SET + + +imdb = Cinemagoer() + + +async def movie( + update: Update, + context: ContextTypes.DEFAULT_TYPE +) -> None: + context_init(context) + + chooser = context.chat_data["users"][0] or None + username = update.message.from_user.username + + if "@"+username != chooser: + raise error.TelegramError(MOVIE_ANOTHER_USER.format(user=chooser)) + + if context.args == []: + raise error.TelegramError(MOVIE_NOT_PROVIDED) + + if len(context.args) > 1: + raise error.TelegramError(EXPECTED_ONE_MOVIE) + + movie_id = context.args[0] + + await update.message.reply_text(FETCHING_MOVIE.format(id=movie_id)) + + try: + movie = imdb.get_movie(movie_id) + except: + raise error.TelegramError(FETCHING_ERROR) + + movie_dict = dict( + title=movie.data.get("title"), + id=movie.getID(), + user=update.effective_user.username, + poster=movie.data.get("cover url") + ) + + if len(context.chat_data["movies"]) > 0 and \ + context.chat_data["movies"][-1]["user"] == username: + context.chat_data["movies"][-1] = movie_dict + else: + context.chat_data["movies"].append(movie_dict) + + await update.message.reply_text( + MOVIE_SET.format(title=movie_dict["title"], user=movie_dict["user"]) + ) + + +async def movies( + update: Update, + context: ContextTypes.DEFAULT_TYPE +) -> None: + context_init(context) + + movies = context.chat_data["movies"].copy() + + for movie in movies: + if movie["poster"] or None: + del movie["poster"] + + await update.message.reply_text(pformat(movies)) + + +async def remove_movies( + update: Update, + context: ContextTypes.DEFAULT_TYPE +) -> None: + context_init(context) + + if context.args == []: + raise error.TelegramError(MOVIE_NOT_PROVIDED) + + movies = context.chat_data["movies"] + + for movie_id in context.args: + for movie in movies: + if movie["id"] == movie_id: + context.chat_data["movies"].remove(movie) + await update.message.reply_text( + MOVIE_REMOVE.format(title=movie["title"], id=["movie.id"]) + ) + diff --git a/commands/users.py b/commands/users.py index 413b681..497d7dd 100644 --- a/commands/users.py +++ b/commands/users.py @@ -14,8 +14,9 @@ from telegram.ext import ContextTypes from collections import deque from strings import USER_NOT_PROVIDED, USERS_ADDED, USERS_REMOVED, \ - EXPECTED_ONE_USER, USER_SET, USER_ADD, USER_REMOVE -from utils import context_init + EXPECTED_ONE_USER, USER_SET, USER_ADD, USER_REMOVE, ADD_MORE_USERS, \ + NEXT_MOVIE_USER, USER_NOT_FOUND, USER_CHOOSE +from utils import context_init, create_users_string async def set_users( @@ -53,7 +54,23 @@ async def list_users( ) -> None: context_init(context) - await update.message.reply_text(context.chat_data["users"]) + users = context.chat_data["users"] + + await update.message.reply_markdown(create_users_string(users)) + + +async def who_is_next( + update: Update, + context: ContextTypes.DEFAULT_TYPE +) -> None: + context_init(context) + + users = context.chat_data["users"] + + if len(users) > 0: + await update.message.reply_text(NEXT_MOVIE_USER.format(user=users[0])) + else: + await update.message.reply_text(ADD_MORE_USERS) async def remove_users( @@ -91,9 +108,16 @@ async def chooser_user( chooser = context.args[0] users = deque(context.chat_data["users"]) - chooser_index = users.index(chooser) + try: + chooser_index = users.index(chooser) + except ValueError: + raise error.TelegramError(USER_NOT_FOUND.format(user=chooser)) + users.rotate(-chooser_index) + users = list(users) + + context.chat_data["users"] = users - context.chat_data["users"] = list(users) + await update.message.reply_text(USER_CHOOSE.format(user=users[0])) - await update.message.reply_text(context.chat_data["users"]) + await update.message.reply_markdown(create_users_string(users)) diff --git a/main.py b/main.py index 10c05a7..6035f76 100644 --- a/main.py +++ b/main.py @@ -16,7 +16,7 @@ from dotenv import load_dotenv import os import logging -from strings import INVALID_COMMAND +from strings import INVALID_COMMAND, UNDEFINED_ERROR from persistence import Persistence import commands @@ -38,9 +38,12 @@ async def unknown(update: Update, context: ContextTypes.DEFAULT_TYPE): async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None: logging.error("Exception:", exc_info=context.error) + error_message = context.error.message \ + if hasattr(context.error, "message") else UNDEFINED_ERROR + await context.bot.send_message( chat_id=update.effective_chat.id, - text=context.error.message + text=error_message ) if __name__ == "__main__": @@ -52,11 +55,17 @@ if __name__ == "__main__": app.add_handler(CommandHandler("about", commands.about)) # Movie commands + app.add_handler(CommandHandler("movie", commands.movie)) + app.add_handler(CommandHandler("movies", commands.movies)) + app.add_handler(CommandHandler("movies_remove", commands.remove_movies)) + + # Users commands app.add_handler(CommandHandler("set", commands.set_users)) app.add_handler(CommandHandler("add", commands.add_users)) app.add_handler(CommandHandler("list", commands.list_users)) app.add_handler(CommandHandler("remove", commands.remove_users)) app.add_handler(CommandHandler("chooser", commands.chooser_user)) + app.add_handler(CommandHandler("next", commands.who_is_next)) app.add_handler(MessageHandler(filters.COMMAND, unknown)) app.add_error_handler(error_handler) diff --git a/requirements.txt b/requirements.txt index b07e05a..7f8ae4c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ python-dotenv==0.20.0 python-telegram-bot==21.6 +cinemagoer==2023.5.1 diff --git a/strings.py b/strings.py index 84171b8..4a51308 100644 --- a/strings.py +++ b/strings.py @@ -10,11 +10,29 @@ # INVALID_COMMAND = "Invalid command. Available commands: /add /list /remove /chooser /about" + +MOVIE_ANOTHER_USER = "Movie should choose another user ({user})" +MOVIE_NOT_PROVIDED = "Movie is not provided" +MOVIE_NOT_FOUND = "Movie not found on IMDB" +EXPECTED_ONE_MOVIE = "Expected only one movie" +FETCHING_MOVIE = "Movie with ID {id} is being fetched: https://imdb.com/title/tt{id}/" +FETCHING_ERROR = "Couldn't fetch movie or it is not found. Provide IMDB id, for example: 0133093" +MOVIE_REMOVE = "Movie \"{title}\" with id {id} has been removed" +MOVIE_SET = "Movie \"{title}\" proposed by {user} is succesfully set as next to watch" + +USER_NOT_FOUND = "Provided user ({user}) not found. Check /list" USER_NOT_PROVIDED = "User(s) is not provided" EXPECTED_ONE_USER = "Expected only one user" +ADD_MORE_USERS = "There is no users added. You can add people who can choose movies using /add nickname" +NEXT_MOVIE_USER = "Next movie choice is up to {user}" + USER_ADD = "User {user} has been added" USERS_ADDED = "Users have been added successfully. Use /list to view." + USER_REMOVE = "User {user} has been removed. Use /list to view." USERS_REMOVED = "Users has been removed" + USER_SET = "Users have been set successfully. Use /list to view." +USER_CHOOSE = "Next movie should choose: {user}" +UNDEFINED_ERROR = "Exception: something unexpected happened. Check the logs." diff --git a/utils.py b/utils.py index 4b28c03..93ad3ed 100644 --- a/utils.py +++ b/utils.py @@ -13,11 +13,22 @@ from telegram.ext import ContextTypes def context_init(context: ContextTypes.DEFAULT_TYPE): - ''' + """ Initialize chat context with starting values - ''' + """ if "users" not in context.chat_data: context.chat_data["users"]: list[str] = [] + if "movies" not in context.chat_data: + context.chat_data["movies"]: list[dict] = [] + return context + + +def normalize_username(username: str): + return username.replace("@", "") + + +def create_users_string(users: list[str]) -> str: + return "`" + ", ".join(users) + "`" -- cgit v1.2.3