def create_game(update, context): """Create a new game instance for the chat of the user""" user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) # Create either a singleplayer or multiplayer game if chat.type == "private": game_type = BlackJackGame.Type.SINGLEPLAYER elif chat.type == "group" or chat.type == "supergroup": game_type = BlackJackGame.Type.MULTIPLAYER_GROUP else: logger.error("Chat type '{}' not supported!".format(chat.type)) return game = BlackJackGame(gametype=game_type) game.add_player(user_id=user.id, first_name=user.first_name) GameStore().add_game(chat.id, game) # TODO currently the game starts instantly - this should change with multiplayer rooms if game.type == BlackJackGame.Type.SINGLEPLAYER: update.effective_message.reply_text(translator("game_starts_now").format("", get_cards_string(game.dealer, lang_id))) players_turn(update, context) else: text = translator("mp_request_join").format(game.get_player_list()) update.effective_message.reply_text(text=text, reply_markup=get_join_keyboard(game.id, lang_id))
def join_callback(update, context): """ CallbackQueryHandler callback for the 'join' inline button. Adds the executing player to the game of the specific chat """ user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) game = GameStore().get_game(chat.id) if not is_button_affiliated(update, context, game, lang_id): return try: game.add_player(user.id, user.first_name) update.effective_message.edit_text(text=translator("mp_request_join").format(game.get_player_list()), reply_markup=get_join_keyboard(game.id, lang_id)) update.callback_query.answer(translator("mp_join_callback").format(user.first_name)) # If players are full, replace join keyboard with start keyboard if len(game.players) >= game.MAX_PLAYERS: update.effective_message.edit_reply_markup(reply_markup=get_start_keyboard(lang_id)) except errors.GameAlreadyRunningException: remove_inline_keyboard(update, context) update.callback_query.answer(translator("mp_game_already_begun_callback")) except errors.MaxPlayersReachedException: update.effective_message.edit_reply_markup(reply_markup=get_start_keyboard(lang_id)) update.callback_query.answer(translator("mp_max_players_callback")) except errors.PlayerAlreadyExistingException: update.callback_query.answer(translator("mp_already_joined_callback"))
def next_player(update, context): chat = update.effective_chat user = update.effective_user lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) game = GameStore().get_game(chat.id) try: if user.id != game.get_current_player().user_id: update.callback_query.answer(translator("mp_not_your_turn_callback").format(user.first_name)) return remove_inline_keyboard(update, context) game.next_player() except NoPlayersLeftException: # TODO merge messages update.effective_message.reply_text(translator("dealers_cards_are").format(game.dealer.cardvalue, get_cards_string(game.dealer, lang_id)), parse_mode=ParseMode.HTML) evaluation_string = generate_evaluation_string(game, lang_id) newgame_button = InlineKeyboardButton(text=translator("inline_keyboard_newgame"), callback_data="newgame") keyboard = InlineKeyboardMarkup(inline_keyboard=[[newgame_button]]) update.effective_message.reply_text(evaluation_string, reply_markup=keyboard) game.stop(-1) return players_turn(update, context)
def players_turn(update, context): """Execute a player's turn""" chat = update.effective_chat game = GameStore().get_game(chat.id) player = game.get_current_player() user_mention = html_mention(user_id=player.user_id, first_name=player.first_name) lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) logger.info("Player's turn: {}".format(player)) player_cards = get_cards_string(player, lang_id) # Check if player already has 21 or a BlackJack before their move. If so, automatically jump to the next player. # We need reply_text here, because we must send a new message (this is the first message for the player)! if player.has_blackjack(): text = (translator("your_cards_are") + "\n\n" + translator("got_blackjack")).format(user_mention, player.cardvalue, player_cards) update.effective_message.reply_text(text=text, parse_mode=ParseMode.HTML, reply_markup=None) next_player(update, context) elif player.cardvalue == 21: text = (translator("your_cards_are") + "\n\n" + translator("got_21")).format(user_mention, player.cardvalue, player_cards) update.effective_message.reply_text(text=text, parse_mode=ParseMode.HTML, reply_markup=None) next_player(update, context) else: text = translator("your_cards_are").format(user_mention, player.cardvalue, player_cards) update.effective_message.reply_text(text=text, parse_mode=ParseMode.HTML, reply_markup=get_game_keyboard(game.id, lang_id))
def answer_comment_cmd(update, context): # Answer to admins only in English, because we don't save admin languages yet text = update.effective_message.text reply_to_message = update.message.reply_to_message text = text.replace("/answer ", "") # Error handling if reply_to_message is None or update.message.reply_to_message.from_user.id != context.bot.id: update.message.reply_text("⚠ You need to reply to the user's comment!") return if update.message.reply_to_message.text is None: update.message.reply_text("⚠ You replied to a non text message!") return try: # Parse user data from the message user_info = reply_to_message.text.split("\n")[-1] except Exception as e: update.message.reply_text("⚠ An unexpected error occurred!") logger.error( "While parsing user data, the following exception occurred: {}". format(e)) return user = user_info.split(" | ") if type(user) != list or len(user) != 6: update.message.reply_text( "⚠ Can't parse user data from the message you replied to! Please reply to a comment!" ) logger.warning("Can't parse user data from message: {}".format( reply_to_message.text)) return chat_id = user[0] if not re.match(r"^\d+$", chat_id): update.message.reply_text("⚠ Malformed chat_id!") logger.error("Malformed chat_id: {}".format(chat_id)) return translator = Translator(chat_id) user_reply = translator("reply_from_maintainer").format(text) # The following errors can easily happen here: # Have no rights to send a message -> Missing permissions # Forbidden: bot was blocked by the user -> User blocked the bot context.bot.send_message(chat_id=chat_id, text=user_reply) update.message.reply_text(text="I sent your comment to the user!") notify_admins( "An admin replied to the comment by\n\n{}\n\nwith:\n\n{}".format( user_info, text), context)
def admin_check(update, context): user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) if user.id in Database().get_admins(): return func(update, context) else: update.message.reply_text(translator("no_permission")) logging.warning( "User {} ({}, @{}) tried to use admin function '{}'!".format( user.id, user.first_name, user.username, func.__name__))
def get_join_keyboard(game_id, lang_id): """ Generates a join keyboard translated into the given language :param game_id: A unique identifier for each game :param lang_id: The language identifier for a specific chat :return: """ translator = Translator(lang_id) join_button = InlineKeyboardButton(text=translator("inline_keyboard_join"), callback_data="join_{}".format(game_id)) start_button = InlineKeyboardButton( text=translator("inline_keyboard_start"), callback_data="start_{}".format(game_id)) return InlineKeyboardMarkup(inline_keyboard=[[join_button, start_button]])
def wrapper(update, context, *args, **kwargs): chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) try: game = GameStore().get_game(chat.id) except NoActiveGameException: remove_inline_keyboard(update, context) update.effective_message.reply_text( translator("mp_no_created_game_callback")) return return func(update, context)
def stop_cmd(update, context): """Stops a game for a specific user""" user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) game = GameStore().get_game(chat.id) try: game.stop(user.id) update.effective_message.reply_text(translator("game_ended")) except errors.InsufficientPermissionsException: update.effective_message.reply_text( translator("mp_only_creator_can_end"))
def start_cmd(update, context): """Handles messages contianing the /start command. Starts a game for a specific user""" user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) Database().add_user(user.id, user.language_code, user.first_name, user.last_name, user.username) try: GameStore().get_game(update.effective_chat.id) # TODO notify user that there is a running game already? except NoActiveGameException: # If there is no game, we create one create_game(update, context)
def get_game_keyboard(game_id, lang_id): """Generates a game keyboard translated into the given language :param game_id: A unique identifier for each game :param lang_id: The language identifier for a specific chat :return: """ translator = Translator(lang_id) one_more_button = InlineKeyboardButton( text=translator("inline_keyboard_hit"), callback_data="hit_{}".format(game_id)) no_more_button = InlineKeyboardButton( text=translator("inline_keyboard_stand"), callback_data="stand_{}".format(game_id)) stop_button = InlineKeyboardButton(text="Stop", callback_data="stop_{}".format(game_id)) return InlineKeyboardMarkup( inline_keyboard=[[one_more_button, no_more_button]])
def start_callback(update, context): """Starts a game that has been created already""" user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) try: game = GameStore().get_game(update.effective_chat.id) if not is_button_affiliated(update, context, game, lang_id): return except NoActiveGameException: update.callback_query.answer(translator("mp_no_created_game_callback")) remove_inline_keyboard(update, context) return try: game.start(user.id) update.callback_query.answer(translator("mp_starting_game_callback")) except errors.GameAlreadyRunningException: update.callback_query.answer( translator("mp_game_already_begun_callback")) return except errors.NotEnoughPlayersException: update.callback_query.answer( translator("mp_not_enough_players_callback")) return except errors.InsufficientPermissionsException: update.callback_query.answer( translator("mp_only_creator_start_callback").format( user.first_name)) return if game.type != BlackJackGame.Type.SINGLEPLAYER: players_are = translator("mp_players_are") for player in game.players: players_are += "👤{}\n".format(player.first_name) players_are += "\n" else: players_are = "" update.effective_message.edit_text( translator("game_starts_now").format( players_are, get_cards_string(game.dealer, lang_id))) players_turn(update, context)
def _generate_evaluation_string_mp(game, lang_id): list_won, list_tie, list_losses = game.evaluation() message = "" translator = Translator(lang_id) dealer_name = translator("dealer_name") if len(list_won) > 0: message += translator("eval_heading_wins") + "\n" message += _get_player_list_string(list_won, dealer_name) # 🔃 if len(list_tie) > 0: message += "\n\n{}\n".format(translator("eval_heading_ties")) message += _get_player_list_string(list_tie, dealer_name) if len(list_losses) > 0: message += "\n\n{}\n".format(translator("eval_heading_losses")) message += _get_player_list_string(list_losses, dealer_name) return message
def _generate_evaluation_string_sp(game, lang_id): list_won, list_tie, list_losses = game.evaluation() message = "" join_str = "\n{} - {}" player = game.players[0] translator = Translator(lang_id) if len(list_won) == 1: if game.dealer.busted: # Dealer busted, you won message += translator("dealer_busted") else: # Closer to 21 message += translator("closer_to_21") message += "\n" message += join_str.format(player.first_name, player.cardvalue) message += join_str.format(translator("dealer_name"), game.dealer.cardvalue) elif len(list_tie) == 1: # Same value as dealer message += translator("tied_with_dealer") message += "\n" message += join_str.format(player.first_name, player.cardvalue) message += join_str.format(translator("dealer_name"), game.dealer.cardvalue) elif len(list_losses) == 1: if player.busted: # busted message += translator("you_busted") elif game.dealer.has_blackjack(): message += translator("dealer_got_blackjack") else: message += translator("dealer_got_21") message += "\n" message += join_str.format(translator("dealer_name"), game.dealer.cardvalue) message += join_str.format(player.first_name, player.cardvalue) return message
def hit_callback(update, context): """ CallbackQueryHandler callback for the 'hit' inline button. Draws a card for you. """ user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) game = GameStore().get_game(chat.id) if not is_button_affiliated(update, context, game, lang_id): return player = game.get_current_player() user_mention = html_mention(user_id=player.user_id, first_name=player.first_name) try: if user.id != player.user_id: update.callback_query.answer(translator("mp_not_your_turn_callback").format(user.first_name)) return game.draw_card() player_cards = get_cards_string(player, lang_id) text = translator("your_cards_are").format(user_mention, player.cardvalue, player_cards) update.effective_message.edit_text(text=text, parse_mode=ParseMode.HTML, reply_markup=get_game_keyboard(game.id, lang_id)) except errors.PlayerBustedException: player_cards = get_cards_string(player, lang_id) text = (translator("your_cards_are") + "\n\n" + translator("you_busted")).format(user_mention, player.cardvalue, player_cards) update.effective_message.edit_text(text=text, parse_mode=ParseMode.HTML, reply_markup=None) next_player(update, context) except errors.PlayerGot21Exception: player_cards = get_cards_string(player, lang_id) if player.has_blackjack(): text = (translator("your_cards_are") + "\n\n" + translator("got_blackjack")).format(user_mention, player.cardvalue, player_cards) update.effective_message.edit_text(text=text, parse_mode=ParseMode.HTML, reply_markup=None) next_player(update, context) elif player.cardvalue == 21: text = (translator("your_cards_are") + "\n\n" + translator("got_21")).format(user_mention, player.cardvalue, player_cards) update.effective_message.edit_text(text=text, parse_mode=ParseMode.HTML, reply_markup=None) next_player(update, context)
def stop_cmd(update, context): """Stops a game for a specific user""" user = update.effective_user chat = update.effective_chat lang_id = Database().get_lang_id(chat.id) translator = Translator(lang_id=lang_id) game = GameStore().get_game(chat.id) user_id = user.id try: if chat.type == "group" or chat.type == "supergroup": # If yes, get the chat admins admins = context.bot.get_chat_administrators(chat_id=chat.id) # if user.id in chat admin IDs, let them end the game with admin powers if user.id in [x.user.id for x in admins]: user_id = -1 game.stop(user_id) update.effective_message.reply_text(translator("game_ended")) except errors.InsufficientPermissionsException: update.effective_message.reply_text(translator("mp_only_creator_can_end"))
def get_start_keyboard(lang_id): translator = Translator(lang_id) start_button = InlineKeyboardButton( text=translator("inline_keyboard_start"), callback_data="start") return InlineKeyboardMarkup(inline_keyboard=[[start_button]])