From 4cdffffa5f4d213719735f86681a76538ab2f8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannes=20H=C3=B6ke?= Date: Sun, 22 May 2016 17:02:27 +0200 Subject: [PATCH] Optional multi-translations --- bot.py | 130 +++++++++++++++++++++------- game.py | 1 + game_manager.py | 2 + locales/de_DE/LC_MESSAGES/unobot.po | 39 ++++++--- locales/unobot.pot | 23 ++++- results.py | 15 ++-- simple_commands.py | 6 +- utils.py | 87 ++++++++++++++----- 8 files changed, 229 insertions(+), 74 deletions(-) diff --git a/bot.py b/bot.py index e1bdefa..d3b1ea2 100644 --- a/bot.py +++ b/bot.py @@ -41,6 +41,7 @@ from utils import _, __, send_async, answer_async, user_locale, game_locales, \ from shared_vars import botan, gm, updater, dispatcher import simple_commands, settings +from simple_commands import help logging.basicConfig( @@ -134,11 +135,11 @@ def leave_game(bot, update): except NotEnoughPlayersError: gm.end_game(chat, user) - send_async(bot, chat.id, text=__("Game ended!")) + send_async(bot, chat.id, text=__("Game ended!", game.translate)) else: send_async(bot, chat.id, - text=__("Okay. Next Player: {name}").format( + text=__("Okay. Next Player: {name}", game.translate).format( name=display_name(game.current_player.user)), reply_to_message_id=update.message.message_id) @@ -185,20 +186,20 @@ def status_update(bot, update): chat = update.message.chat if update.message.left_chat_member: - try: - user = update.message.left_chat_member - except KeyError: - return + user = update.message.left_chat_member try: gm.leave_game(user, chat) + game = gm.player_for_user_in_chat(user, chat).game + except NoGameInChatError: pass except NotEnoughPlayersError: gm.end_game(chat, user) - send_async(bot, chat.id, text=__("Game ended!")) + send_async(bot, chat.id, text=__("Game ended!", game.translate)) else: - send_async(bot, chat.id, text=__("Removing {name} from the game") + send_async(bot, chat.id, text=__("Removing {name} from the game", + game.translate) .format(name=display_name(user))) @@ -230,6 +231,13 @@ def start_game(bot, update, args): game.play_card(game.last_card) game.started = True + first_message = ( + __("First player: {name}\n" + "Use /close to stop people from joining the game.\n" + "Enable multi-translations with /enable_translations", + game.translate) + .format(name=display_name(game.current_player.user))) + @run_async def send_first(): """Send the first card and player""" @@ -239,12 +247,7 @@ def start_game(bot, update, args): timeout=TIMEOUT) bot.sendMessage(chat.id, - text=__("First player: {name}\n" - "Use /close to stop people from " - "joining the game.") - .format( - name=display_name(game.current_player.user) - ), + text=first_message, timeout=TIMEOUT) send_first() @@ -327,6 +330,63 @@ def open_game(bot, update): return +@user_locale +def enable_translations(bot, update): + """Handler for the /enable_translations command""" + chat = update.message.chat + user = update.message.from_user + games = gm.chatid_games.get(chat.id) + + if not games: + send_async(bot, chat.id, + text=_("There is no running game in this chat.")) + return + + game = games[-1] + + if game.owner.id == user.id: + game.translate = True + send_async(bot, chat.id, text=_("Enabled multi-translations. " + "Disable with /disable_translations")) + return + + else: + send_async(bot, chat.id, + text=_("Only the game creator ({name}) can do that") + .format(name=game.owner.first_name), + reply_to_message_id=update.message.message_id) + return + + +@user_locale +def disable_translations(bot, update): + """Handler for the /disable_translations command""" + chat = update.message.chat + user = update.message.from_user + games = gm.chatid_games.get(chat.id) + + if not games: + send_async(bot, chat.id, + text=_("There is no running game in this chat.")) + return + + game = games[-1] + + if game.owner.id == user.id: + game.translate = False + send_async(bot, chat.id, text=_("Disabled multi-translations. " + "Enable them again with " + "/enable_translations")) + return + + else: + send_async(bot, chat.id, + text=_("Only the game creator ({name}) can do that") + .format(name=game.owner.first_name), + reply_to_message_id=update.message.message_id) + return + + @game_locales @user_locale def skip_player(bot, update): @@ -365,7 +425,7 @@ def skip_player(bot, update): send_async(bot, chat.id, text=__("Waiting time to skip this player has " "been reduced to {time} seconds.\n" - "Next player: {name}") + "Next player: {name}", game.translate) .format(time=skipped_player.waiting_time, name=display_name(next_player.user))) game.turn() @@ -376,7 +436,7 @@ def skip_player(bot, update): send_async(bot, chat.id, text=__("{name1} was skipped four times in a row " "and has been removed from the game.\n" - "Next player: {name2}") + "Next player: {name2}", game.translate) .format(name1=display_name(skipped_player.user), name2=display_name(next_player.user))) @@ -384,11 +444,12 @@ def skip_player(bot, update): send_async(bot, chat.id, text=__("{name} was skipped four times in a row " "and has been removed from the game.\n" - "The game ended.") + "The game ended.", game.translate) .format(name=display_name(skipped_player.user))) gm.end_game(chat.id, skipped_player.user) + @game_locales @user_locale def reply_to_query(bot, update): @@ -420,10 +481,10 @@ def reply_to_query(bot, update): add_draw(player, results) else: - add_pass(results) + add_pass(results, game) if game.last_card.special == c.DRAW_FOUR and game.draw_counter: - add_call_bluff(results) + add_call_bluff(results, game) playable = player.playable_cards() added_ids = list() # Duplicates are not allowed @@ -479,7 +540,7 @@ def process_result(bot, update): return elif int(anti_cheat) != last_anti_cheat: send_async(bot, chat.id, - text=__("Cheat attempt by {name}") + text=__("Cheat attempt by {name}", game.translate) .format(name=display_name(player.user))) return elif result_id == 'call_bluff': @@ -498,7 +559,7 @@ def process_result(bot, update): if game in gm.chatid_games.get(chat.id, list()): send_async(bot, chat.id, - text=__("Next player: {name}") + text=__("Next player: {name}", game.translate) .format(name=display_name(game.current_player.user))) @@ -510,7 +571,8 @@ def reset_waiting_time(bot, player): player.waiting_time = 90 send_async(bot, chat.id, text=__("Waiting time for {name} has been reset to 90 " - "seconds").format(name=display_name(player.user))) + "seconds", player.game.translate) + .format(name=display_name(player.user))) def do_play_card(bot, player, result_id): @@ -529,11 +591,12 @@ def do_play_card(bot, player, result_id): if len(player.cards) == 0: send_async(bot, chat.id, - text=__("{name} won!").format(name=user.first_name)) + text=__("{name} won!", game.translate) + .format(name=user.first_name)) try: gm.leave_game(user, chat) except NotEnoughPlayersError: - send_async(bot, chat.id, text=__("Game ended!")) + send_async(bot, chat.id, text=__("Game ended!", game.translate)) gm.end_game(chat, user) if botan: @@ -551,7 +614,8 @@ def do_draw(bot, player): player.draw() except DeckEmptyError: send_async(bot, player.game.chat.id, - text=__("There are no more cards in the deck.")) + text=__("There are no more cards in the deck.", + game.translate)) if (game.last_card.value == c.DRAW_TWO or game.last_card.special == c.DRAW_FOUR) and \ @@ -566,26 +630,30 @@ def do_call_bluff(bot, player): if player.prev.bluffing: send_async(bot, chat.id, - text=__("Bluff called! Giving 4 cards to {name}") + text=__("Bluff called! Giving 4 cards to {name}", + game.translate) .format(name=player.prev.user.first_name)) try: player.prev.draw() except DeckEmptyError: send_async(bot, player.game.chat.id, - text=__("There are no more cards in the deck.")) + text=__("There are no more cards in the deck.", + game.translate)) else: game.draw_counter += 2 send_async(bot, chat.id, - text=__("{name1} didn't bluff! Giving 6 cards to {name2}") + text=__("{name1} didn't bluff! Giving 6 cards to {name2}", + game.translate) .format(name1=player.prev.user.first_name, name2=player.user.first_name)) try: player.draw() except DeckEmptyError: send_async(bot, player.game.chat.id, - text=__("There are no more cards in the deck.")) + text=__("There are no more cards in the deck.", + game.translate)) game.turn() @@ -600,6 +668,10 @@ dispatcher.add_handler(CommandHandler('join', join_game)) dispatcher.add_handler(CommandHandler('leave', leave_game)) dispatcher.add_handler(CommandHandler('open', open_game)) dispatcher.add_handler(CommandHandler('close', close_game)) +dispatcher.add_handler(CommandHandler('enable_translations', + enable_translations)) +dispatcher.add_handler(CommandHandler('disable_translations', + disable_translations)) dispatcher.add_handler(CommandHandler('skip', skip_player)) dispatcher.add_handler(MessageHandler([Filters.status_update], status_update)) dispatcher.add_error_handler(error) diff --git a/game.py b/game.py index cbfc575..f25726a 100644 --- a/game.py +++ b/game.py @@ -34,6 +34,7 @@ class Game(object): started = False owner = None open = True + translate = False def __init__(self, chat): self.chat = chat diff --git a/game_manager.py b/game_manager.py index a505ade..0013cba 100644 --- a/game_manager.py +++ b/game_manager.py @@ -77,6 +77,8 @@ class GameManager(object): self.leave_game(user, chat) except NoGameInChatError: pass + except NotEnoughPlayersError: + self.end_game(chat, user) player = Player(game, user) diff --git a/locales/de_DE/LC_MESSAGES/unobot.po b/locales/de_DE/LC_MESSAGES/unobot.po index 89a0232..9ce018d 100644 --- a/locales/de_DE/LC_MESSAGES/unobot.po +++ b/locales/de_DE/LC_MESSAGES/unobot.po @@ -18,7 +18,7 @@ #: bot.py:224 msgid "" msgstr "" -"Project-Id-Version: mau_mau_bot 0.1\n" +"Project-Id-Version: uno_bot 0.1\n" "Report-Msgid-Bugs-To: uno@jhoeke.de\n" "POT-Creation-Date: 2016-05-19 22:38+0200\n" "PO-Revision-Date: 2016-05-21 21:16+0200\n" @@ -32,8 +32,7 @@ msgstr "" "X-Generator: Gtranslator 2.91.6\n" #: bot.py:60 -msgid "" -"Follow these steps:\n" +msgid "Follow these steps:\n" "\n" "1. Add this bot to a group\n" "2. In the group, start a new game with /new or join an already running game " @@ -49,9 +48,13 @@ msgid "" "player takes more than 90 seconds to play, you can use /skip to skip that " "player.\n" "\n" +"Language and other settings: /settings\n" "Other commands (only game creator):\n" "/close - Close lobby\n" "/open - Open lobby\n" +"/enable_translations - Translate relevant texts into all " +"languages spoken in a game\n" +"/disable_translations - Use English for those texts\n" "\n" "Experimental: Play in multiple groups at the same time. Press the " "Current game: ... button and select the group you want to play " @@ -78,9 +81,13 @@ msgstr "" "benutze /leave. Wenn ein Spieler länger als 90 Sekunden braucht, kannst du " "ihn mit /skip überspringen.\n" "\n" +"Sprache und andere Einstellungen: /settings\n" "Weitere Kommandos (nur Spiel-Ersteller):\n" "/close - Lobby schließen\n" "/open - Lobby öffnen\n" +"/enable_translations - Übersetze relevante Texte in alle im Spiel gesprochenen" +" Sprachen\n" +"/disable_translations - Verwende Englisch für diese Texte\n" "\n" "Experimentell: Spiele in mehreren Gruppen gleichzeitig. Um die " "Gruppe, in der du deine Karte spielen willst, auszuwählen, tippe auf den " @@ -170,15 +177,14 @@ msgstr "Das Spiel hat bereits begonnen" #: bot.py:281 msgid "At least two players must /join the game before you can start it" -msgstr "" -"Es müssen mindestens zwei Spieler dem Spiel beitreten, bevor du es starten " -"kannst" +msgstr "Es müssen mindestens zwei Spieler dem Spiel beitreten, bevor du es " +"starten kannst" #: bot.py:297 -#, python-format -msgid "" -"First player: {name}\n" -"Use /close to stop people from joining the game." +#, python-format, fuzzy +msgid "First player: {name}\n" +"Use /close to stop people from joining the game.\n" +"Enable multi-translations with /enable_translations" msgstr "" "Erster Spieler: {name}\n" "Benutze /close, um zu verhindern, dass weitere Spieler beitreten." @@ -201,6 +207,17 @@ msgstr "" msgid "Only the game creator ({name}) can do that." msgstr "Dies kann nur der Ersteller des Spiels ({name}) tun." +#: bot.py:349 +#, python-format +msgid "Enabled multi-translations. Disable with /disable_translations" +msgstr "Multi-Übersetzungen aktiviert. Deaktivieren mit /disable_translations" + +#: bot.py:377 +#, python-format +msgid "Disabled multi-translations. Enable them again with /enable_translations" +msgstr "Multi-Übersetzungen deaktiviert. Aktiviere sie wieder mit " +"/enable_translations" + #: bot.py:368 msgid "Opened the lobby. New players may /join the game." msgstr "Lobby geöffnet. Neue Spieler können nun beitreten." @@ -259,7 +276,7 @@ msgid "Cheat attempt by %s" msgstr "{name} hat versucht zu schummeln!" #: bot.py:562 -msgid "Next player: " +msgid "Next player: {name}" msgstr "Nächster Spieler: {name}" #: bot.py:572 diff --git a/locales/unobot.pot b/locales/unobot.pot index 2742045..0005894 100644 --- a/locales/unobot.pot +++ b/locales/unobot.pot @@ -19,7 +19,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: mau_mau_bot 0.1\n" +"Project-Id-Version: uno_bot 0.1\n" "Report-Msgid-Bugs-To: uno@jhoeke.de\n" "POT-Creation-Date: 2016-05-19 22:38+0200\n" "PO-Revision-Date: 2016-05-19 22:38+0200\n" @@ -32,6 +32,7 @@ msgstr "" #: bot.py:60 +#, fuzzy msgid "Follow these steps:\n" "\n" "1. Add this bot to a group\n" @@ -48,10 +49,13 @@ msgid "Follow these steps:\n" "player takes more than 90 seconds to play, you can use /skip to skip that " "player.\n" "\n" +"Language and other settings: /settings\n" "Other commands (only game creator):\n" "/close - Close lobby\n" "/open - Open lobby\n" -"\n" +"/enable_translations - Translate relevant texts into all " +"languages spoken in a game\n" +"/disable_translations - Use English for those texts\n\n" "Experimental: Play in multiple groups at the same time. Press the " "Current game: ... button and select the group you want to play " "a card in.\n" @@ -136,9 +140,10 @@ msgstr "" #: bot.py:297 -#, python-format +#, python-format, fuzzy msgid "First player: {name}\n" -"Use /close to stop people from joining the game." +"Use /close to stop people from joining the game.\n" +"Enable multi-translations with /enable_translations" msgstr "" #: bot.py:321 @@ -159,6 +164,16 @@ msgstr "" msgid "Only the game creator ({name}) can do that." msgstr "" +#: bot.py:349 +#, python-format +msgid "Enabled multi-translations. Disable with /disable_translations" +msgstr "" + +#: bot.py:377 +#, python-format +msgid "Disabled multi-translations. Enable them again with /enable_translations" +msgstr "" + #: bot.py:368 msgid "Opened the lobby. New players may /join the game." msgstr "" diff --git a/results.py b/results.py index 47fa31b..722656d 100644 --- a/results.py +++ b/results.py @@ -100,9 +100,10 @@ def add_draw(player, results): Sticker( "draw", sticker_file_id=c.STICKERS['option_draw'], input_message_content= - InputTextMessageContent(__('Drawing 1 card') + InputTextMessageContent(__('Drawing 1 card', player.game.translate) if n == 1 else - __('Drawing {number} cards') + __('Drawing {number} cards', + player.game.translate) .format(number=n)) ) ) @@ -120,24 +121,26 @@ def add_gameinfo(game, results): ) -def add_pass(results): +def add_pass(results, game): """Add option to pass""" results.append( Sticker( "pass", sticker_file_id=c.STICKERS['option_pass'], - input_message_content=InputTextMessageContent(__('Pass')) + input_message_content=InputTextMessageContent(__('Pass', + game.translate)) ) ) -def add_call_bluff(results): +def add_call_bluff(results, game): """Add option to call a bluff""" results.append( Sticker( "call_bluff", sticker_file_id=c.STICKERS['option_bluff'], input_message_content= - InputTextMessageContent(__("I'm calling your bluff!")) + InputTextMessageContent(__("I'm calling your bluff!", + game.translate)) ) ) diff --git a/simple_commands.py b/simple_commands.py index 5fb9d13..51c0770 100644 --- a/simple_commands.py +++ b/simple_commands.py @@ -39,9 +39,13 @@ help_text = ("Follow these steps:\n\n" "Players can join the game at any time. To leave a game, " "use /leave. If a player takes more than 90 seconds to play, " "you can use /skip to skip that player.\n\n" + "Language and other settings: /settings\n" "Other commands (only game creator):\n" "/close - Close lobby\n" - "/open - Open lobby\n\n" + "/open - Open lobby\n" + "/enable_translations - Translate relevant texts into all " + "languages spoken in a game\n" + "/disable_translations - Use English for those texts\n\n" "Experimental: Play in multiple groups at the same time. " "Press the Current game: ... button and select the " "group you want to play a card in.\n" diff --git a/utils.py b/utils.py index 1d0676f..9d93152 100644 --- a/utils.py +++ b/utils.py @@ -29,6 +29,7 @@ from telegram.ext.dispatcher import run_async import locales from database import db_session from user_setting import UserSetting +from shared_vars import gm strategy = PackageStrategy('unobot', locales) application = registry.register(strategy) @@ -38,27 +39,28 @@ logger = logging.getLogger(__name__) TIMEOUT = 2.5 -def __(string): +def __(string, multi_translate): """Translates text into all locales on the stack""" translations = list() locales = list() - while True: - translation = _(string) - - if translation not in translations: - translations.append(translation) - - l = _.code + if not multi_translate: + _.push('en_US') + translations.append(_(string)) _.pop() - if l is None: - break - else: - locales.append(l) + else: + while _.code: + translation = _(string) - for l in reversed(locales): - _.push(l) + if translation not in translations: + translations.append(translation) + + locales.append(_.code) + _.pop() + + for l in reversed(locales): + _.push(l) return '\n'.join(translations) # TODO @@ -126,12 +128,16 @@ def user_locale(func): @wraps(func) @db_session def wrapped(bot, update, *pargs, **kwargs): + user, chat = _user_chat_from_update(update) + with db_session: - us = UserSetting.get(id=update.message.from_user.id) + us = UserSetting.get(id=user.id) + if us: _.push(us.lang) else: _.push('en_US') + result = func(bot, update, *pargs, **kwargs) _.pop() return result @@ -141,15 +147,50 @@ def user_locale(func): def game_locales(func): @wraps(func) @db_session - def wrapped(*pargs, **kwargs): - num_locales = 0 - for loc in ('en_US', 'de_DE'): # TODO: Get user locales from Database - _.push(loc) - num_locales += 1 + def wrapped(bot, update, *pargs, **kwargs): + user, chat = _user_chat_from_update(update) + player = gm.player_for_user_in_chat(user, chat) + locales = list() - result = func(*pargs, **kwargs) + if player: + for player in player.game.players: + us = UserSetting.get(id=player.user.id) - for i in range(num_locales): + if us: + loc = us.lang + else: + loc = 'en_US' + + if loc in locales: + continue + + _.push(loc) + locales.append(loc) + + result = func(bot, update, *pargs, **kwargs) + + for i in locales: _.pop() return result - return wrapped \ No newline at end of file + return wrapped + + +def _user_chat_from_update(update): + + try: + user = update.message.from_user + chat = update.message.chat + except (NameError, AttributeError): + try: + user = update.inline_query.from_user + chat = gm.userid_current[user.id].game.chat + except KeyError: + chat = None + except (NameError, AttributeError): + try: + user = update.chosen_inline_result.from_user + chat = gm.userid_current[user.id].game.chat + except (NameError, AttributeError): + chat = None + + return user, chat