Index: commands/__init__.py
===================================================================
--- commands/__init__.py	(revision 0d48ea28aeb8a6290d2aeab202efa9c8d6e30fe3)
+++ commands/__init__.py	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
@@ -11,3 +11,5 @@
 
 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
Index: commands/movie.py
===================================================================
--- commands/movie.py	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
+++ commands/movie.py	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
@@ -0,0 +1,105 @@
+# This file is part of python-cinema-club-bot
+# contributed in 2024 by Mikhail Kirillov (~w96k) <w96k@runbox.com>
+
+# 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:
+# <http://creativecommons.org/publicdomain/zero/1.0/>
+
+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"])
+                )
+
Index: commands/users.py
===================================================================
--- commands/users.py	(revision 0d48ea28aeb8a6290d2aeab202efa9c8d6e30fe3)
+++ commands/users.py	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
@@ -15,6 +15,7 @@
 
 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
 
 
@@ -54,5 +55,21 @@
     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)
 
 
@@ -92,8 +109,15 @@
     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"] = list(users)
+    context.chat_data["users"] = users
 
-    await update.message.reply_text(context.chat_data["users"])
+    await update.message.reply_text(USER_CHOOSE.format(user=users[0]))
+
+    await update.message.reply_markdown(create_users_string(users))
Index: main.py
===================================================================
--- main.py	(revision 0d48ea28aeb8a6290d2aeab202efa9c8d6e30fe3)
+++ main.py	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
@@ -17,5 +17,5 @@
 import logging
 
-from strings import INVALID_COMMAND
+from strings import INVALID_COMMAND, UNDEFINED_ERROR
 from persistence import Persistence
 import commands
@@ -39,7 +39,10 @@
     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
     )
 
@@ -53,4 +56,9 @@
 
     # 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))
@@ -58,4 +66,5 @@
     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))
Index: requirements.txt
===================================================================
--- requirements.txt	(revision 0d48ea28aeb8a6290d2aeab202efa9c8d6e30fe3)
+++ requirements.txt	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
@@ -1,2 +1,3 @@
 python-dotenv==0.20.0
 python-telegram-bot==21.6
+cinemagoer==2023.5.1
Index: strings.py
===================================================================
--- strings.py	(revision 0d48ea28aeb8a6290d2aeab202efa9c8d6e30fe3)
+++ strings.py	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
@@ -11,10 +11,28 @@
 
 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."
Index: utils.py
===================================================================
--- utils.py	(revision 0d48ea28aeb8a6290d2aeab202efa9c8d6e30fe3)
+++ utils.py	(revision 662342863e37e8661f818218df7bb72bc18bf4eb)
@@ -14,10 +14,21 @@
 
 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) + "`"
