From 466c11007b2840677678db6c47af0d333a1e0dba Mon Sep 17 00:00:00 2001 From: Rian Felipe Celestrino <39718272+RianFC@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:23:44 -0300 Subject: [PATCH] First commit unu Powered by Hydrogram version 1.0.0-beta --- .github/workflows/unit-tests.yaml | 27 ++ .gitignore | 6 + bot.py | 18 + card.py | 56 +++ cards/classic.json | 152 ++++++++ cards/colorblind.json | 179 +++++++++ cards/minimalist.json | 190 ++++++++++ config-example.py | 12 + db.py | 48 +++ deck.py | 23 ++ game.py | 24 ++ locales.py | 93 +++++ locales/en-US.yml | 69 ++++ locales/pt-BR.yml | 69 ++++ plugins/game.py | 607 ++++++++++++++++++++++++++++++ plugins/settings.py | 209 ++++++++++ plugins/start.py | 68 ++++ plugins/sudos.py | 467 +++++++++++++++++++++++ spetials/minimalist.py | 32 ++ version.py | 13 + 20 files changed, 2362 insertions(+) create mode 100644 .github/workflows/unit-tests.yaml create mode 100644 .gitignore create mode 100644 bot.py create mode 100644 card.py create mode 100644 cards/classic.json create mode 100644 cards/colorblind.json create mode 100644 cards/minimalist.json create mode 100644 config-example.py create mode 100644 db.py create mode 100644 deck.py create mode 100644 game.py create mode 100644 locales.py create mode 100644 locales/en-US.yml create mode 100644 locales/pt-BR.yml create mode 100644 plugins/game.py create mode 100644 plugins/settings.py create mode 100644 plugins/start.py create mode 100644 plugins/sudos.py create mode 100644 spetials/minimalist.py create mode 100644 version.py diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml new file mode 100644 index 0000000..bbbb2de --- /dev/null +++ b/.github/workflows/unit-tests.yaml @@ -0,0 +1,27 @@ +name: Python Unit Tests + +on: [push, pull_request] # Define when the workflow should be triggered + +jobs: + test: + name: Run Unit Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run unittest + run: | + echo '{}' > config.json # Create an empty config file + python -m unittest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..227c0a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +config.py +*.session* +*database.sqlite* +__pycache__/ +*/__pycache__/ +config.py diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..3b1d8ee --- /dev/null +++ b/bot.py @@ -0,0 +1,18 @@ +from config import bot +from hydrogram import idle +from tortoise import run_async +from version import ascii_art +from db import connect_database +import os + + +async def main(): + os.system("clear") + print(ascii_art) + await connect_database() + await bot.start() + await idle() + await bot.stop() + + +run_async(main()) diff --git a/card.py b/card.py new file mode 100644 index 0000000..407d48a --- /dev/null +++ b/card.py @@ -0,0 +1,56 @@ +import os +import json + +RED = "r" +BLUE = "b" +GREEN = "g" +YELLOW = "y" +BLACK = "x" + +COLORS = (RED, BLUE, GREEN, YELLOW) + +COLOR_ICONS = {RED: "❤️", BLUE: "💙", GREEN: "💚", YELLOW: "💛", BLACK: "⬛️"} + +# Values +ZERO = "0" +ONE = "1" +TWO = "2" +THREE = "3" +FOUR = "4" +FIVE = "5" +SIX = "6" +SEVEN = "7" +EIGHT = "8" +NINE = "9" +DRAW_TWO = "draw" +REVERSE = "reverse" +SKIP = "skip" + +VALUES = ( + ZERO, + ONE, + TWO, + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT, + NINE, + DRAW_TWO, + REVERSE, + SKIP, +) + +# Special cards +CHOOSE = "colorchooser" +DRAW_FOUR = "draw_four" + +SPECIALS = (CHOOSE, DRAW_FOUR) + +cards = {} +for filename in os.listdir("cards"): + if filename.endswith(".json"): + name = filename.split(".")[0] + with open(f"cards/{filename}", "r") as f: + cards[name] = json.load(f) diff --git a/cards/classic.json b/cards/classic.json new file mode 100644 index 0000000..b01c2ac --- /dev/null +++ b/cards/classic.json @@ -0,0 +1,152 @@ +{ + "STICKERS": { + "b_0": "CAACAgQAAxkDAAI372NtY-V641fF6HhAA4Vuc6CbI_LeAALZAQACX1eZAAEqnpNt3SpG_ysE", + "b_1": "CAACAgQAAxkDAAI38GNtY-UvkNQN3h5p5n_dfNbhPV9HAALbAQACX1eZAAHluPl_BVzaDisE", + "b_2": "CAACAgQAAxkDAAI38WNtY-X4Gvnxt4mofZ-Uv_zmGWHRAALdAQACX1eZAAEFe5JBdpP-cysE", + "b_3": "CAACAgQAAxkDAAI38mNtY-av7Gm6hUEdRs_mONWGzKoGAALfAQACX1eZAAFQJXWHQ2D7uisE", + "b_4": "CAACAgQAAxkDAAI382NtY-YXxHbN1MfXSl6FbzwgWq5vAALhAQACX1eZAAHo1SP4devY_ysE", + "b_5": "CAACAgQAAxkDAAI39GNtY-dNEOn0i1luuPjPOHvqyasxAALjAQACX1eZAALf6g-FruzaKwQ", + "b_6": "CAACAgQAAxkDAAI39WNtY-df9ew41xXE6ARS3VHDKg0NAALlAQACX1eZAAHwMoU1Nb4OgisE", + "b_7": "CAACAgQAAxkDAAI39mNtY-ftENXWBUBNqNTomh-NeufNAALnAQACX1eZAAFOBAnoop1fWisE", + "b_8": "CAACAgQAAxkDAAI392NtY-idvjst_LSKlwP2cEDnS3WpAALpAQACX1eZAAHmKrizqjwJ3isE", + "b_9": "CAACAgQAAxkDAAI3-GNtY-jEw-hh0ei6OxSl2r4DehmIAALrAQACX1eZAAHvul-ZztVWiisE", + "b_draw": "CAACAgQAAxkDAAI3-WNtY-nrtJj_c48YtjbPwydARdwJAALtAQACX1eZAAGdURg9n6qvEysE", + "b_skip": "CAACAgQAAxkDAAI3-mNtY-kVI0dIVd38sOvZrZmtRCv_AALxAQACX1eZAAHAf0ks_Y82JysE", + "b_reverse": "CAACAgQAAxkDAAI3-2NtY-p4_EUUTVDYKX12SMcKA9IbAALvAQACX1eZAAFjAZc535XzNSsE", + "g_0": "CAACAgQAAxkDAAI3_GNtY-qvO3V8NwHojOpf8aIpbnYvAAL3AQACX1eZAAH7m-CsNWDzBSsE", + "g_1": "CAACAgQAAxkDAAI3_WNtY-r28bGOeJGKL7ZtEwUrWXzfAAL5AQACX1eZAAFVNSG--aqs9CsE", + "g_2": "CAACAgQAAxkDAAI3_mNtY-sjqfdB5nu7iKPFqHRItFerAAL7AQACX1eZAAHDX5Qn7VbSdCsE", + "g_3": "CAACAgQAAxkDAAI3_2NtY-ueCVLB_KL8Xz0itFJGWNbYAAL9AQACX1eZAAGwUxSSKSNPaisE", + "g_4": "CAACAgQAAxkDAAI4AAFjbWPsRRrrb0KdkF5SGCO87ni9sAAC_wEAAl9XmQABARICqk9L7OArBA", + "g_5": "CAACAgQAAxkDAAI4AWNtY-zlRyWdS69Z4bcwBgklRcBEAAIBAgACX1eZAAGN2wN5nVhf3ysE", + "g_6": "CAACAgQAAxkDAAI4AmNtY-zXK3F2NTz-XaFeDk2rsP7NAAIDAgACX1eZAAFaJA80kw1XfSsE", + "g_7": "CAACAgQAAxkDAAI4A2NtY-0dqOmBW9-XK_BbtXg0OLRaAAIFAgACX1eZAAGDbLTCiNGLBisE", + "g_8": "CAACAgQAAxkDAAI4BGNtY-2kF7oUCmvU_AbU9lmudtZqAAIHAgACX1eZAAGnWrRTRZj7gSsE", + "g_9": "CAACAgQAAxkDAAI4BWNtY-60BpqwiJQ8-p93unknqHi2AAIJAgACX1eZAAHODOPdhwzltysE", + "g_draw": "CAACAgQAAxkDAAI4BmNtY-53H6EJgbUQSeEpguubOevXAAILAgACX1eZAAFWg06uGplHVysE", + "g_skip": "CAACAgQAAxkDAAI4B2NtY-5VirooDDZAWu4ENrVBBoFHAAIPAgACX1eZAAHn-hBXxRvYQisE", + "g_reverse": "CAACAgQAAxkDAAI4CGNtY-_G8b0fBt0N3OBgx9CIwJziAAINAgACX1eZAAFMYqmCS3vfySsE", + "r_0": "CAACAgQAAxkDAAI4CWNtY-9LOKHb1FqCn3GmqxOYCo_fAAIRAgACX1eZAAHK9atgT_cu_isE", + "r_1": "CAACAgQAAxkDAAI4CmNtY_Dxb_ivl-VHFRDPgHVOilCVAAITAgACX1eZAAH_6pt2airFESsE", + "r_2": "CAACAgQAAxkDAAI4C2NtY_B9bP3cd73NvBd-Un8yZTYzAAIVAgACX1eZAAHQrmSSeMDfgCsE", + "r_3": "CAACAgQAAxkDAAI4DGNtY_Hqk5RPHjNn50jy_ImBPYZLAAIXAgACX1eZAAFeHWWPa-piRysE", + "r_4": "CAACAgQAAxkDAAI4DWNtY_HqY4wNkPulTWHIY9d2Fep-AAIZAgACX1eZAAE7VUWywkd3KCsE", + "r_5": "CAACAgQAAxkDAAI4DmNtY_Lb9j5Qi5RVPEaSW3uZWAnlAAIbAgACX1eZAAF1s0b9V-PUJCsE", + "r_6": "CAACAgQAAxkDAAI4D2NtY_Kklm1t7E0KShmWTbXEwnpNAAIdAgACX1eZAAF8hSz11exIUisE", + "r_7": "CAACAgQAAxkDAAI4EGNtY_LoR07j-LayjpoVlEPLCCe0AAIfAgACX1eZAAEVnCo1RKSqnCsE", + "r_8": "CAACAgQAAxkDAAI4EWNtY_OrIOu5PPIUTZ-cn0FBFcT2AAIhAgACX1eZAAEhXezQrbzKOisE", + "r_9": "CAACAgQAAxkDAAI4EmNtY_PI6uILsPHkkyIDFp4ivFBJAAIjAgACX1eZAAHN4GBkUaxpqisE", + "r_draw": "CAACAgQAAxkDAAI4E2NtY_SNrUaYiRbAIEi9c_X-veafAAIlAgACX1eZAAGZvG1zNp2cVisE", + "r_skip": "CAACAgQAAxkDAAI4FGNtY_SrNSCK9k9FO9Xji2fb9LJMAAIpAgACX1eZAAFprUDwYHBu3SsE", + "r_reverse": "CAACAgQAAxkDAAI4FWNtY_V41t8UX4XtxugfwVMibbqLAAInAgACX1eZAAGay7EvXnoVZisE", + "y_0": "CAACAgQAAxkDAAI4FmNtY_XYaAevT9wxGiAxI1n6e_spAAIrAgACX1eZAAG1mgAB2D5sIc8rBA", + "y_1": "CAACAgQAAxkDAAI4F2NtY_aD1zsrQYWtYoeePhDN1bcvAAItAgACX1eZAAHqNCCjuSEQjisE", + "y_2": "CAACAgQAAxkDAAI4GGNtY_Y9kN6nzxvk8KwX8SnwTntmAAIvAgACX1eZAAH4u547rBAiBCsE", + "y_3": "CAACAgQAAxkDAAI4GWNtY_dJwM67rmUFcLEtByedoFJdAAIxAgACX1eZAAFBQ00TMrpMeisE", + "y_4": "CAACAgQAAxkDAAI4GmNtY_d6JUufI61BWnqI4DTVRxMVAAIzAgACX1eZAAF7IOqIuGqyDSsE", + "y_5": "CAACAgQAAxkDAAI4G2NtY_dxij19aBCA7Tjf5ytWzXgNAAI1AgACX1eZAAHyIiYzI-E-LisE", + "y_6": "CAACAgQAAxkDAAI4HGNtY_hPQ2iuWWmADOUYR-P-nNVFAAI3AgACX1eZAAH_E8fuZ374hysE", + "y_7": "CAACAgQAAxkDAAI4HWNtY_jp9tXZ3lpAV83tzDcazcA4AAI5AgACX1eZAAHPK6qSI6Ku_CsE", + "y_8": "CAACAgQAAxkDAAI4HmNtY_kQwEGUW6F38bBIYXfspzarAAI7AgACX1eZAAHXiL4XwJi0eysE", + "y_9": "CAACAgQAAxkDAAI4H2NtY_kJ_ofl80XkaVobKpd-IgqQAAI9AgACX1eZAAGG_opl6vQSOCsE", + "y_draw": "CAACAgQAAxkDAAI4IGNtY_qbKj2mnuJVlTai4F6se8MNAAI_AgACX1eZAAFrjyuhcA2ksysE", + "y_skip": "CAACAgQAAxkDAAI4IWNtY_rKy-RTeKjfZT0RAYNNreVhAAJDAgACX1eZAAF1m63alvMoxysE", + "y_reverse": "CAACAgQAAxkDAAI4ImNtY_vaX0rQZ_5ZUeFTpMa2ZQABOwACQQIAAl9XmQABCHpDm7MPbakrBA", + "draw_four": "CAACAgQAAxkDAAI4I2NtY_vr1Fa4_Q2Y6dxOopNX7sSsAAL1AQACX1eZAAHXOgABZUCgVkkrBA", + "colorchooser": "CAACAgQAAxkDAAI4JGNtY_vpncCbuHH2xDLokQWxUAXSAALzAQACX1eZAAHI5jbpFQE9bCsE", + "option_draw": "CAACAgQAAxkDAAI4JWNtY_zry4NT2JAlWjTryYiuec4nAAL4AgACX1eZAAH-TdXSlvEa2ysE", + "option_pass": "CAACAgQAAxkDAAI4JmNtY_yMlr6rB3UdTikR3zFCk8kVAAL6AgACX1eZAAFuilR5QnD-VysE", + "option_bluff": "CAACAgQAAxkDAAI4J2NtY_2Dmt5Mi4iZhsUh32OeNVe7AALKAgACX1eZAAHBw478rNqN0CsE", + "option_info": "CAACAgQAAxkDAAI4KGNtY_3tO0Sxhu5NzF1UA3tdUnklAALEAgACX1eZAAGi2Qy93IIQwisE" + }, + "STICKERS_GREY": { + "b_0": "CAACAgQAAxkDAAI4KWNtY_3SM2AGtecbGE8XDjlWvcKxAAJFAgACX1eZAAHwXYFNZhQaIysE", + "b_1": "CAACAgQAAxkDAAI4KmNtY_7zNsvijvvGZAJmuxcYVgizAAJHAgACX1eZAAF_ZxC64wgdNCsE", + "b_2": "CAACAgQAAxkDAAI4K2NtY_4z7XEHPzcliqJth5G3ds6vAAJJAgACX1eZAAF-GuNgJ25IAAErBA", + "b_3": "CAACAgQAAxkDAAI4LGNtY_9ZPE9nPCPJQ0Rjf_zOkTsiAAJLAgACX1eZAAHIJQ71XJ39mCsE", + "b_4": "CAACAgQAAxkDAAI4LWNtY_--OWOFczobsp10PPj5p9pZAAJNAgACX1eZAAEjmR2mhJ8SsSsE", + "b_5": "CAACAgQAAxkDAAI4LmNtZAABTkAAAT7kcgxZkdA3rcZmxM0AAk8CAAJfV5kAASN8DC8z_yexKwQ", + "b_6": "CAACAgQAAxkDAAI4L2NtZAABOSkvi7YF9opHBHILrQukJwACUQIAAl9XmQABv35eqFpp188rBA", + "b_7": "CAACAgQAAxkDAAI4MGNtZAABcb94kfODfzBiW7R6caIITgACUwIAAl9XmQABv8VaivrtncwrBA", + "b_8": "CAACAgQAAxkDAAI4MWNtZAEPZcxI8yZZJ7mtvLEhRyQyAAJVAgACX1eZAAF8hUb4bS_NdCsE", + "b_9": "CAACAgQAAxkDAAI4MmNtZAHG55HKa6LNKc496jAPrUCzAAJXAgACX1eZAAGXAmJ0BKvi1ysE", + "b_draw": "CAACAgQAAxkDAAI4M2NtZALeN87Xgly5X7j5XK0dfaznAAJZAgACX1eZAAFS-DsDXK7zdisE", + "b_skip": "CAACAgQAAxkDAAI4NGNtZAJR4ZxfKgABx3HNLp-9w8fNagACXQIAAl9XmQABc7AYk0bGSHorBA", + "b_reverse": "CAACAgQAAxkDAAI4NWNtZAKP0DU5ZIh-4eID9fwEqWDhAAJbAgACX1eZAAHRLf8w4EEJfysE", + "g_0": "CAACAgQAAxkDAAI4NmNtZAMTsoTxk-Gzg61XUbgiWmuDAAJjAgACX1eZAAG_c8FzjSBlOCsE", + "g_1": "CAACAgQAAxkDAAI4N2NtZAOMsFWlo1a6VbET_L4Z33qjAAJlAgACX1eZAAH2R3CHmHduZCsE", + "g_2": "CAACAgQAAxkDAAI4OGNtZASmomincPijzQaGuhzS4NT3AAJnAgACX1eZAAHB14u8vZ5pjSsE", + "g_3": "CAACAgQAAxkDAAI4OWNtZATXrH2F0kmklBKkx5-yLbqeAAJpAgACX1eZAAFaZGnJmMcN9CsE", + "g_4": "CAACAgQAAxkDAAI4OmNtZARrtuTkDtrmFwSWGCMNNyzVAAJrAgACX1eZAAF3KxLEqQq8KysE", + "g_5": "CAACAgQAAxkDAAI4O2NtZAXsq9mIqylmXkuqblUSZ_s5AAJtAgACX1eZAAGObwogvTEInCsE", + "g_6": "CAACAgQAAxkDAAI4PGNtZAXYyNLL6UnAXV2J5fcYDSjcAAJvAgACX1eZAAEpOGFMRnLGmSsE", + "g_7": "CAACAgQAAxkDAAI4PWNtZAYp5RXbOKe2_RQkDLNHRnQsAAJxAgACX1eZAAEe_yu4DVELEisE", + "g_8": "CAACAgQAAxkDAAI4PmNtZAZuRr1ubCO9SBPYf5uVwxOVAAJzAgACX1eZAAH26plyNxWZuCsE", + "g_9": "CAACAgQAAxkDAAI4P2NtZAZ-4ux439AfgakLYhj7NkL7AAJ1AgACX1eZAAGrwYoTMk8UPSsE", + "g_draw": "CAACAgQAAxkDAAI4QGNtZAcDJt3SZBIXhpzxAw-0pCjgAAJ3AgACX1eZAAFnlFIJWhbZIysE", + "g_skip": "CAACAgQAAxkDAAI4QWNtZAdu6EvL3cTpvKgvVvS5TM8oAAJ7AgACX1eZAAFO5CqgPxquYSsE", + "g_reverse": "CAACAgQAAxkDAAI4QmNtZAhYEij-J99P6WZprlvTrO1FAAJ5AgACX1eZAAE9cd3JVwlSEisE", + "r_0": "CAACAgQAAxkDAAI4Q2NtZAhJMx2vsEJ0VqZf4K4vnICEAAJ9AgACX1eZAAEZAg2nRervSCsE", + "r_1": "CAACAgQAAxkDAAI4RGNtZAggA5W5F360ygp-Kt5511ZGAAJ_AgACX1eZAAFtLPMD6heoDysE", + "r_2": "CAACAgQAAxkDAAI4RWNtZAneP8mxTRUYpxCIcSZxrRzaAAKBAgACX1eZAAGuvzFU0Su89SsE", + "r_3": "CAACAgQAAxkDAAI4RmNtZAkm-2Z3z4dgngqsNQKlAAEUIgACgwIAAl9XmQABBRY8MBWexokrBA", + "r_4": "CAACAgQAAxkDAAI4R2NtZAr32JAr0Q5mSzPrZuPKAAEMAAOFAgACX1eZAAHZFzRnwree-ysE", + "r_5": "CAACAgQAAxkDAAI4SGNtZAo06aPW8Bt2bEfhuAwYIAihAAKHAgACX1eZAAHsdpjtu9I2ISsE", + "r_6": "CAACAgQAAxkDAAI4SWNtZArDcMo4iVhDv3V2PkjmODGWAAKJAgACX1eZAAG2D__a-tqZBSsE", + "r_7": "CAACAgQAAxkDAAI4SmNtZAsNc-unKFxRAUfRgRpIu8zGAAKLAgACX1eZAAGXaAtw5YFztSsE", + "r_8": "CAACAgQAAxkDAAI4S2NtZAtXBBjw_QmbUnPCqOjcPciqAAKNAgACX1eZAAGkCOaURWQl8CsE", + "r_9": "CAACAgQAAxkDAAI4TGNtZAxdvNd9s7XbaETEDpraDSB8AAKPAgACX1eZAAH-WS6bmv9CgSsE", + "r_draw": "CAACAgQAAxkDAAI4TWNtZAz-9sSylYycGwF82_5ceXLOAAKRAgACX1eZAAF2dldgt636fysE", + "r_skip": "CAACAgQAAxkDAAI4TmNtZAwwZq3xqWgdKCELX9yXNNDHAAKVAgACX1eZAAGedr9LYgVebCsE", + "r_reverse": "CAACAgQAAxkDAAI4T2NtZA1_h1jpVObJt7ZnGWC0EJu_AAKTAgACX1eZAAECR8T0lu-KmysE", + "y_0": "CAACAgQAAxkDAAI4UGNtZA3XHBEqHJ4oD2s1vu019fCAAAKXAgACX1eZAALmpUbJzkaKKwQ", + "y_1": "CAACAgQAAxkDAAI4UWNtZA70oPDw_EYnua3I_yHnoU0HAAKZAgACX1eZAAGB_02-C22PkysE", + "y_2": "CAACAgQAAxkDAAI4UmNtZA73r_BBydbo0QL4Lrp6zzRgAAKbAgACX1eZAAHVmZUJxJwqmCsE", + "y_3": "CAACAgQAAxkDAAI4U2NtZA7ITY2cWf3hZhbqbRFA2rznAAKdAgACX1eZAAGnajv8YZQj-ysE", + "y_4": "CAACAgQAAxkDAAI4VGNtZA_w89jaIqKJT3mJ3jf4sNfqAAKfAgACX1eZAAEmxeENpAa35SsE", + "y_5": "CAACAgQAAxkDAAI4VWNtZA9pJt03yLW1UVqmabBu03CRAAKhAgACX1eZAAH2evQmPPzx8isE", + "y_6": "CAACAgQAAxkDAAI4VmNtZBBLaA_cEcY1-cmo4oRl7kFUAAKjAgACX1eZAAGYOfBpuoRg_CsE", + "y_7": "CAACAgQAAxkDAAI4V2NtZBC1E-0IzKlEqkiFlLtGQ2djAAKlAgACX1eZAAFYxwrVWROuiysE", + "y_8": "CAACAgQAAxkDAAI4WGNtZBDuCE40_AciHh4BlfOxvd4EAAKnAgACX1eZAAF10j1L6rASCSsE", + "y_9": "CAACAgQAAxkDAAI4WWNtZBERcGe9cafGmVQMrn--6VyEAAKpAgACX1eZAAGV1nEmuqjoJCsE", + "y_draw": "CAACAgQAAxkDAAI4WmNtZBHW7Ik5O4gDp80GEnME_8opAAKrAgACX1eZAAGfJ2XK_ooNFisE", + "y_skip": "CAACAgQAAxkDAAI4W2NtZBLpZ4ilI48Wl42H2--LNZleAAKvAgACX1eZAAEVSSkTcHxJXCsE", + "y_reverse": "CAACAgQAAxkDAAI4XGNtZBJeXdZLAWEB9hQVadvba2mLAAKtAgACX1eZAAEiP9aakPoiDysE", + "draw_four": "CAACAgQAAxkDAAI4XWNtZBOEsZAZxOHFAttWBmLf5WSOAAJhAgACX1eZAAHWx9PCWaCqkysE", + "colorchooser": "CAACAgQAAxkDAAI4XmNtZBPR9vYmNzz7P7Hq24wrLE16AAJfAgACX1eZAAH4WHYrSCRGIisE" + }, + "CARDS": { + "SPECIALS": ["draw_four","colorchooser"], + "SPECIALS_INFO": { + "draw_four": [4,"."], + "colorchooser": [4,"^(?!.*draw).*$"] + }, + "THEME_CARDS": [], + "COLORS": ["b","g","r","y"], + "COLOR_ICONS": { + "b": "🟦", + "g": "🟩", + "r": "🟥", + "y": "🟨", + "x": "❓" + }, + "VALUES": ["0","1","2","3","4","5","6","7","8","9","draw","skip","reverse"], + "VALUES_ICONS": { + "0": "0️⃣", + "1": "1️⃣", + "2": "2️⃣", + "3": "3️⃣", + "4": "4️⃣", + "5": "5️⃣", + "6": "6️⃣", + "7": "7️⃣", + "8": "8️⃣", + "9": "9️⃣", + "draw": "+2", + "skip": "🚫", + "reverse": "🔁", + "colorchooser": "🌈", + "draw_four": "+4" + } + } +} \ No newline at end of file diff --git a/cards/colorblind.json b/cards/colorblind.json new file mode 100644 index 0000000..7326074 --- /dev/null +++ b/cards/colorblind.json @@ -0,0 +1,179 @@ +{ + "STICKERS": { + "b_0": "CAACAgQAAxkBAAIH7WWViIfZuQW92q6yTE6BQfXj-UWDAAL4EAACSPyQUbrUYl3Db1A4HgQ", + "b_1": "CAACAgQAAxkBAAIH8GWViIgsVZgNZsZYg_RaskmJCKJZAAJxEwAC74SYUVPWLgtyo7g5HgQ", + "b_2": "CAACAgQAAxkBAAIH82WViIklUQmKnrock1_yNBqqMhVJAALTFAACpWGZUfKsVIBvG1-PHgQ", + "b_3": "CAACAgQAAxkBAAIH9mWViIrIYOGeBv0MxhC6FWldvYQyAAI2DQACf7GYUd6_wh0X-J7THgQ", + "b_4": "CAACAgQAAxkBAAIH-WWViIvJL4MFx0Q10L4Y7J6tTgzOAAKVDgACNlqZUVwgv-lUHlwXHgQ", + "b_5": "CAACAgQAAxkBAAIH_GWViIuh16wb9Nb28sRG5uSV6ab5AAKuDAAC4vmZUe8QFnwIbmnlHgQ", + "b_6": "CAACAgQAAxkBAAIH_2WViIzaZQQttfD0R03vcmRiKyQWAAIOFQAC4ZGZUZBi-YQ7FzMEHgQ", + "b_7": "CAACAgQAAxkBAAIIAmWViI0Ur0TXzVyntbIFiUF70ngzAAIjEQAC_92YUS5W3f2SSBmDHgQ", + "b_8": "CAACAgQAAxkBAAIIBWWViI5BriPVz45ADl9kKBP73JTkAALbDQACgTqRUcbtaxxXgXaHHgQ", + "b_9": "CAACAgQAAxkBAAIICGWViI8P2ffbeW1mFnEG4sjeJ58MAAK9DQACtEyZUStZpJE_-CIpHgQ", + "b_draw": "CAACAgQAAxkBAAIIC2WViJGgz0IpM8FbgtwmtZVLeuj6AAKXDgACMX-QUc9ZKhgoHC3BHgQ", + "b_skip": "CAACAgQAAxkBAAIIDmWViJIq_Ib0JQ7UQ5clASSpFNuyAAL5DAACBtmYUaW1SWeyUM1RHgQ", + "b_reverse": "CAACAgQAAxkBAAIIEWWViJOq0hbTlIjYSLZw-7ct8OquAAKiDwACoMmZUQlCnlahydSdHgQ", + "g_0": "CAACAgQAAxkBAAIIFGWViJbNqv-dn_a5j2LuBSFhGKOPAAJODgAChDGYUXTuNH-98HjGHgQ", + "g_1": "CAACAgQAAxkBAAIIF2WViJd4nHU1y1ztouceDT1cfDHuAAJCDgACGSCYUaWyJ_2c21fCHgQ", + "g_2": "CAACAgQAAxkBAAIIGmWViJipH6ctdppuXX21KV-hUEl2AALYFQAC57-YUYQ2I9KNnFuJHgQ", + "g_3": "CAACAgQAAxkBAAIIHWWViJm1DwWP9SJoSSP-YCAvhaGjAAKnDgACM72YUcA7PwoPafsHHgQ", + "g_4": "CAACAgQAAxkBAAIIIGWViJrgai_4_1GWuNTq1SrwHoY6AAJGEAACoXuQURzAtqUt8N9oHgQ", + "g_5": "CAACAgQAAxkBAAIII2WViJuQB3lW1m169IogS1bu-dd2AAICDwACpcuYUcnKDYX_Jtu_HgQ", + "g_6": "CAACAgQAAxkBAAIIJmWViJ02ZaybQAg82BqZBXYSRQ_xAAJWFgAC0ruZUaKBGr8SQK5WHgQ", + "g_7": "CAACAgQAAxkBAAIIKWWViJ55lDw6091FOyalYKntdBRkAALZEgACtxyYUSP7wshU0BuyHgQ", + "g_8": "CAACAgQAAxkBAAIILGWViKIstE4URA2mdsL1qn6GVf-cAALoDAAC6-aYUcFFqsyKY5ZrHgQ", + "g_9": "CAACAgQAAxkBAAIIL2WViKVGxSTn0Do5onLQ3Ofzka4LAAITEQACw2SZUWvwNocY7CyCHgQ", + "g_draw": "CAACAgQAAxkBAAIIMmWViKdf2wdcUNpvrsc_u8cyzSpnAAKEDwACvFiZUf1yHkmnTQABuh4E", + "g_skip": "CAACAgQAAxkBAAIINWWViKiQLKGbJaNtyCoxw_Qyc0lDAAJsFwACqKeRUQ7CQm0NF-TaHgQ", + "g_reverse": "CAACAgQAAxkBAAIIOGWViKlE1G7Dq9BINwhi5e17RV_dAAIyEAAC9U6YUYfbzgshjtx6HgQ", + "r_0": "CAACAgQAAxkBAAIIO2WViKrN7GshtcQ44nYDgpXlvqQ9AALoDwACf9yRUc9YeqIFH_eMHgQ", + "r_1": "CAACAgQAAxkBAAIIPmWViKsB7yhwRpa-SkjkqIfqiDFeAALlDQACD4iZUTH5uVAjzxGtHgQ", + "r_2": "CAACAgQAAxkBAAIIQWWViKx4deWPc3S0fDskZsyKZ5D1AALWDQAC4yOZUSRCxWE3j0MwHgQ", + "r_3": "CAACAgQAAxkBAAIIRGWViK6lgng5gfXIYThsk1g76KS0AAKWEAACrLiZUdai-hpZ9Ab-HgQ", + "r_4": "CAACAgQAAxkBAAIIR2WViK_83kuSnMJ8nFSoi6VDxkAFAAIJGgACp_-RUWeXwAIvWuOnHgQ", + "r_5": "CAACAgQAAxkBAAIISmWViLIafepcDzkS08BMqkq7rZE3AAJXDwACOZqZUQS_ZNV4v1ylHgQ", + "r_6": "CAACAgQAAxkBAAIITWWViLWZySDZcTEnc66aLKx9URsxAAIdDgAC65SRUfYj7KfqdEFUHgQ", + "r_7": "CAACAgQAAxkBAAIIUGWViLZK1GPE83RP8VZ1G134HkzHAAI1DwACs_WRUXmuoe2TSlD4HgQ", + "r_8": "CAACAgQAAxkBAAIIU2WViLjJbF2IM6EfGh7Fi7Eylv_hAALUEAAC5AORUQ-Iih1SLpa_HgQ", + "r_9": "CAACAgQAAxkBAAIIVmWViLlb9lZw4rLF8zE92W492Y2WAALZDgACrWeQUczrPkLQp9_ZHgQ", + "r_draw": "CAACAgQAAxkBAAIIWWWViLqtM1UOYophQ9hlsIO-6CR2AAK9DwACpm6QUYSaRFKhJlATHgQ", + "r_skip": "CAACAgQAAxkBAAIIXGWViLw3gedDiTOUIuNBrUdTOaWNAAJGDgACnxuZUU4LrF0RllHDHgQ", + "r_reverse": "CAACAgQAAxkBAAIIX2WViL3N06HgWmOmAWwRTuf19J92AALlEAACDz2ZUTz4Rj_YOJ9wHgQ", + "y_0": "CAACAgQAAxkBAAIIYmWViL9dzEK2pkuaVGZDpGLHIh3aAAKtDgACvlaZUc1_r14GfZg4HgQ", + "y_1": "CAACAgQAAxkBAAIIZWWViMDADxyaFhJtHoj2OqLeJuXmAAJyDwACaoqZUd9V5Qje7-LsHgQ", + "y_2": "CAACAgQAAxkBAAIIaGWViMGoxYdaIaoz-5lQlAABhnZCEgACkA4AAuDImFEQ8qjFlcKplR4E", + "y_3": "CAACAgQAAxkBAAIIa2WViMLZtKOb5eAEYKPfbGQT4AXoAAL5DAACauiYUYCpWf5jw3vbHgQ", + "y_4": "CAACAgQAAxkBAAIIbmWViMIZL-SbEFuof3HZiwKxQOM-AAKNDgACY0uYUbzSTuR-DHU0HgQ", + "y_5": "CAACAgQAAxkBAAIIcWWViMP8mHHUUxjQTzZME_UqgOucAAKpDwACaBiYUfX97L9sxA1jHgQ", + "y_6": "CAACAgQAAxkBAAIIdGWViMQBqzJYS4zfGHLbPwac4xMnAAJ1DQAC5ZyYURsb-CbXAZgJHgQ", + "y_7": "CAACAgQAAxkBAAIId2WViMUli35f0n4JpRxn2z7TsxIBAALpDAAClAABmFEjw59G8tfe-R4E", + "y_8": "CAACAgQAAxkBAAIIemWViMYHsYK8A8zotkuQL7N9CUGqAAIBEAACL7aRUSJ8n471ZWKWHgQ", + "y_9": "CAACAgQAAxkBAAIIfWWViMbbYCRyvT1RHSb5LAxtpQFcAALEDQACFDyYUeOeX21QIdIsHgQ", + "y_draw": "CAACAgQAAxkBAAIIgGWViMeJKRPlJmIbw7w6-5EyzSRFAALPDgACdxmZUSEuFfXt6pfwHgQ", + "y_skip": "CAACAgQAAxkBAAIIg2WViMmxpEHw_qCaJGGFGv3FCvokAAI-FgACIZuQUb2Gm0U8uCPvHgQ", + "y_reverse": "CAACAgQAAxkBAAIIhmWViMomxSr2ZFGL4S45yqm9XlMRAAJPEAACqAWZUQlEHDh5aCcPHgQ", + "draw_four": "CAACAgQAAxkBAAIIiWWViM99yb-oIkGdZl69f-zLrDcjAAJhEAACueSZUaapceGNYQHEHgQ", + "colorchooser": "CAACAgQAAxkBAAIIjGWViNByhKosbeWF0U5byjtnNxQCAAKuDgAC9faZUSnH8GIMgTmdHgQ", + "option_draw": "CAACAgQAAxkBAAIIj2WViNj1FXsNWL95ZRUbPZJXJoX7AAL4AgACX1eZAAH-TdXSlvEa2x4E", + "option_pass": "CAACAgQAAxkBAAIIkmWViN0_ybsx490zyv0HFLw2sOzzAAL6AgACX1eZAAFuilR5QnD-Vx4E", + "option_bluff": "CAACAgQAAxkBAAIIlWWViOawFS22nDjGn3cB6svJ7ggkAALKAgACX1eZAAHBw478rNqN0B4E", + "option_info": "CAACAgQAAxkBAAIImGWViPjlUoOmQw7LWRFlgDq2a5u8AALEAgACX1eZAAGi2Qy93IIQwh4E" + }, + "STICKERS_GREY": { + "b_0": "CAACAgQAAxkBAAIIm2WViPpU7jFNtr5MybalgKn-vxneAAJFAgACX1eZAAHwXYFNZhQaIx4E", + "b_1": "CAACAgQAAxkBAAIInmWViPtl57X2VaM5XSb6nr1YGSqZAAJHAgACX1eZAAF_ZxC64wgdNB4E", + "b_2": "CAACAgQAAxkBAAIIoWWViP0cJwFXikbDcrJ00t1K_xJXAAJJAgACX1eZAAF-GuNgJ25IAAEeBA", + "b_3": "CAACAgQAAxkBAAIIpGWViP4BDkd8vKhBxGnHvbjRu_8CAAJLAgACX1eZAAHIJQ71XJ39mB4E", + "b_4": "CAACAgQAAxkBAAIIp2WViP7A3X1R9vjYv0bmvOY-rUPIAAJNAgACX1eZAAEjmR2mhJ8SsR4E", + "b_5": "CAACAgQAAxkBAAIIqmWViP9fFkit6AU8yMRBi0nbvU6CAAJPAgACX1eZAAEjfAwvM_8nsR4E", + "b_6": "CAACAgQAAxkBAAIIrWWViQABRYTEtaNJlrWkobC-DWakRQACUQIAAl9XmQABv35eqFpp188eBA", + "b_7": "CAACAgQAAxkBAAIIsGWViQEyYkmw3Hp_6yEuDEZPg_UGAAJTAgACX1eZAAG_xVqK-u2dzB4E", + "b_8": "CAACAgQAAxkBAAIIs2WViQHf7qbfR_X1dOxCxDyKQVJ6AAJVAgACX1eZAAF8hUb4bS_NdB4E", + "b_9": "CAACAgQAAxkBAAIItmWViQLMnaQOj_1KJ4QB7v7CtgmlAAJXAgACX1eZAAGXAmJ0BKvi1x4E", + "b_draw": "CAACAgQAAxkBAAIIuWWViQQt6MhXUnHrxX6ujGLp9OoUAAJZAgACX1eZAAFS-DsDXK7zdh4E", + "b_skip": "CAACAgQAAxkBAAIIvGWViQXzcWvBVYYuv8gUbBvJmWNzAAJdAgACX1eZAAFzsBiTRsZIeh4E", + "b_reverse": "CAACAgQAAxkBAAIIv2WViQYnXthYtIst_A_lYy9wsFJUAAJbAgACX1eZAAHRLf8w4EEJfx4E", + "g_0": "CAACAgQAAxkBAAIIwmWViQfpxDfIp0PyGemF3zYUIOmgAAJjAgACX1eZAAG_c8FzjSBlOB4E", + "g_1": "CAACAgQAAxkBAAIIxWWViQgejt9YHBrForCHSWyvLnqtAAJlAgACX1eZAAH2R3CHmHduZB4E", + "g_2": "CAACAgQAAxkBAAIIyGWViQnnGcwMI9BRuuD5XkWTP2xQAAJnAgACX1eZAAHB14u8vZ5pjR4E", + "g_3": "CAACAgQAAxkBAAIIy2WViQq0QobbZleDp9ZJpFEbdmrMAAJpAgACX1eZAAFaZGnJmMcN9B4E", + "g_4": "CAACAgQAAxkBAAIIzmWViQpJScW-f97n0rQpOSOYYAeSAAJrAgACX1eZAAF3KxLEqQq8Kx4E", + "g_5": "CAACAgQAAxkBAAII0WWViQteMUvnKyrbndV_TWRSpMuKAAJtAgACX1eZAAGObwogvTEInB4E", + "g_6": "CAACAgQAAxkBAAII1GWViQxEufLsZ6qPMt_0muYHN0c4AAJvAgACX1eZAAEpOGFMRnLGmR4E", + "g_7": "CAACAgQAAxkBAAII12WViQ38LxBaAsypjKdrLorXOGB1AAJxAgACX1eZAAEe_yu4DVELEh4E", + "g_8": "CAACAgQAAxkBAAII2mWViQ5o2Qj2SD-2uqkvRLIkpY74AAJzAgACX1eZAAH26plyNxWZuB4E", + "g_9": "CAACAgQAAxkBAAII3WWViQ4X_eX6VuMP3gmi0Z8WUiIPAAJ1AgACX1eZAAGrwYoTMk8UPR4E", + "g_draw": "CAACAgQAAxkBAAII4GWViQ-wjvGkjoKeN7G_VN7QeAbFAAJ3AgACX1eZAAFnlFIJWhbZIx4E", + "g_skip": "CAACAgQAAxkBAAII42WViRBlqJS24Dpe9jckZjx9NhFrAAJ7AgACX1eZAAFO5CqgPxquYR4E", + "g_reverse": "CAACAgQAAxkBAAII5mWViRGErVTAmwzzL8Gaw6m1uEJWAAJ5AgACX1eZAAE9cd3JVwlSEh4E", + "r_0": "CAACAgQAAxkBAAII6WWViRPUrdPmRLxJB_wPYSEz9VzJAAJ9AgACX1eZAAEZAg2nRervSB4E", + "r_1": "CAACAgQAAxkBAAII7GWViRTYSpKji8JFsJ7grOJpqR63AAJ_AgACX1eZAAFtLPMD6heoDx4E", + "r_2": "CAACAgQAAxkBAAII72WViRSJl1uLaIrhIlvDHumJ9WW7AAKBAgACX1eZAAGuvzFU0Su89R4E", + "r_3": "CAACAgQAAxkBAAII8mWViRWQGHlRWTZ9izrjtocY12kKAAKDAgACX1eZAAEFFjwwFZ7GiR4E", + "r_4": "CAACAgQAAxkBAAII9WWViRYsYsaPYMtVNqEQI14epvFuAAKFAgACX1eZAAHZFzRnwree-x4E", + "r_5": "CAACAgQAAxkBAAII-GWViRc1hhpZfuknLhDcTFE4fAwkAAKHAgACX1eZAAHsdpjtu9I2IR4E", + "r_6": "CAACAgQAAxkBAAII-2WViRhw6hgkC2hTtz5M01PWq4iaAAKJAgACX1eZAAG2D__a-tqZBR4E", + "r_7": "CAACAgQAAxkBAAII_mWViRh0xCPOW62vVVX3eW8qVcYXAAKLAgACX1eZAAGXaAtw5YFztR4E", + "r_8": "CAACAgQAAxkBAAIJAWWViRna-E8mEMrtaJgEN6kadagKAAKNAgACX1eZAAGkCOaURWQl8B4E", + "r_9": "CAACAgQAAxkBAAIJBGWViRpz1cu-hntkeMicpiUWFV1fAAKPAgACX1eZAAH-WS6bmv9CgR4E", + "r_draw": "CAACAgQAAxkBAAIJB2WViRvF9IaFuwLG8Aml_lXaIuVUAAKRAgACX1eZAAF2dldgt636fx4E", + "r_skip": "CAACAgQAAxkBAAIJCmWViRz7AyvfbVzBEGknm69vAlgCAAKVAgACX1eZAAGedr9LYgVebB4E", + "r_reverse": "CAACAgQAAxkBAAIJDWWViR3n0qcDtix_O4w8xWjd9U_1AAKTAgACX1eZAAECR8T0lu-Kmx4E", + "y_0": "CAACAgQAAxkBAAIJEGWViR87oSq_59nWAAFy01lTX4JdxgAClwIAAl9XmQAC5qVGyc5Gih4E", + "y_1": "CAACAgQAAxkBAAIJE2WViSD6-Sq3oTIMqVz-J9zOmIddAAKZAgACX1eZAAGB_02-C22Pkx4E", + "y_2": "CAACAgQAAxkBAAIJFmWViSEE44UOkY4s8r0LLBtvmjcbAAKbAgACX1eZAAHVmZUJxJwqmB4E", + "y_3": "CAACAgQAAxkBAAIJGWWViSGVT72rqzZe98CDc4lP66KnAAKdAgACX1eZAAGnajv8YZQj-x4E", + "y_4": "CAACAgQAAxkBAAIJHGWViSLM42LKJuClX_EVkJIUqhb8AAKfAgACX1eZAAEmxeENpAa35R4E", + "y_5": "CAACAgQAAxkBAAIJH2WViSMRJDYER1V6jctmpe4_LnjBAAKhAgACX1eZAAH2evQmPPzx8h4E", + "y_6": "CAACAgQAAxkBAAIJImWViSTTa8PENvS4oBsjCjyxsOq0AAKjAgACX1eZAAGYOfBpuoRg_B4E", + "y_7": "CAACAgQAAxkBAAIJJWWViSX62G9bAiolY1qdo7wt-lu_AAKlAgACX1eZAAFYxwrVWROuix4E", + "y_8": "CAACAgQAAxkBAAIJKGWViSYRfMC3KTuSH8FBi3GuERF3AAKnAgACX1eZAAF10j1L6rASCR4E", + "y_9": "CAACAgQAAxkBAAIJK2WViSaZDLJsteU3mN3JaZgxbUiQAAKpAgACX1eZAAGV1nEmuqjoJB4E", + "y_draw": "CAACAgQAAxkBAAIJLmWViSfj0w5rRfqv3G7Ehafi-cmIAAKrAgACX1eZAAGfJ2XK_ooNFh4E", + "y_skip": "CAACAgQAAxkBAAIJMWWViSiyvVRcF4fuiitl0tKoZyZ1AAKvAgACX1eZAAEVSSkTcHxJXB4E", + "y_reverse": "CAACAgQAAxkBAAIJNGWViSkfoWKkDlIjj5qEdo5I-W0QAAKtAgACX1eZAAEiP9aakPoiDx4E", + "draw_four": "CAACAgQAAxkBAAIJN2WViSw_fFBkTPJ6F984sRBdaXBDAAJhAgACX1eZAAHWx9PCWaCqkx4E", + "colorchooser": "CAACAgQAAxkBAAIJOmWViS0VcoFfcxzhI8MIvv7NuJZXAAJfAgACX1eZAAH4WHYrSCRGIh4E" + }, + "CARDS": { + "SPECIALS": [ + "draw_four", + "colorchooser" + ], + "SPECIALS_INFO": { + "draw_four": [ + 4, + "." + ], + "colorchooser": [ + 4, + "^(?!.*draw).*$" + ] + }, + "THEME_CARDS": [], + "COLORS": [ + "b", + "g", + "r", + "y" + ], + "COLOR_ICONS": { + "b": "blue", + "g": "green", + "r": "red", + "y": "yellow" + }, + "VALUES": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "draw", + "skip", + "reverse" + ], + "VALUES_ICONS": { + "0": "0️⃣", + "1": "1️⃣", + "2": "2️⃣", + "3": "3️⃣", + "4": "4️⃣", + "5": "5️⃣", + "6": "6️⃣", + "7": "7️⃣", + "8": "8️⃣", + "9": "9️⃣", + "draw": "+2", + "skip": "🚫", + "reverse": "🔁", + "colorchooser": "🌈", + "draw_four": "+4" + } + } +} \ No newline at end of file diff --git a/cards/minimalist.json b/cards/minimalist.json new file mode 100644 index 0000000..2aca3c7 --- /dev/null +++ b/cards/minimalist.json @@ -0,0 +1,190 @@ +{ + "STICKERS": { + "b_0": "CAACAgEAAxkDAAEDe6tllA16pjgNwKfYPSG2fa3-RWCLZgACAwIAAsJ8kUXyk7MLF9xdBh4E", + "b_1": "CAACAgEAAxkDAAEDe6xllA18NuZNUwJvWlQd4oM1ET_DGQACqQMAAnHukEXmnSMIA9EHZh4E", + "b_2": "CAACAgEAAxkDAAEDe61llA1_v5dlOv1S_43ufmWuI9jGKwACXQIAAv_qkEVgclfo22dTXx4E", + "b_3": "CAACAgEAAxkDAAEDe65llA2By62MlWJL1KoSwAABXX6FJ8MAAhIDAAJ0vpBF7s5S-tulgzceBA", + "b_4": "CAACAgEAAxkDAAEDe69llA2EotC8Sxq2BV0_UNLFHiCraAACZwIAAhZvkUVhDXOLwAahsR4E", + "b_5": "CAACAgEAAxkDAAEDe7BllA2GMRqzd1XwtbPsB21ysznGggACKgMAAl3TkUU4NWqARyrisR4E", + "b_6": "CAACAgEAAxkDAAEDe7FllA2IGUW-kUGfNUNo1yLkEhlTKwACZQIAAkEamUXoN8eyf3WbVR4E", + "b_7": "CAACAgEAAxkDAAEDe7JllA2Kl-KJ4CzGnWR6bvmWdsuT3gACPgIAAr9DkUXKlfdmjxSZUx4E", + "b_8": "CAACAgEAAxkDAAEDe7NllA2MWfQmdO8qYnV-xd9Tm8pblgAC2AIAAqIskUUWu-bHbGOF8R4E", + "b_9": "CAACAgEAAxkDAAEDe7RllA2OcK42iYTR8YHEJLvqzqgZCAACkwIAAgbhkEW3VEnx24JUBh4E", + "b_draw": "CAACAgEAAxkDAAEDe7VllA2eJar5j6ts-sL1xCCZBaCKCQACBgMAAkYMkUVyqsDxtyrruR4E", + "b_skip": "CAACAgEAAxkDAAEDe7ZllA2gqoH1ykM-E62WG4JKVb0evwAC5AIAAoMOkUXTMcadXYpdLR4E", + "b_reverse": "CAACAgEAAxkDAAEDe7dllA2ing8hJqUzPezMWG-u9NJhlAACAwMAAlFCkEUfaBJjzqysoR4E", + "g_0": "CAACAgEAAxkDAAEDe7hllA2lB0XmfQX5HerEZEz37IILdwACcQIAAswfkUVCj6ddvIx5iR4E", + "g_1": "CAACAgEAAxkDAAEDe7lllA2nl8MoGoxPec0DhQ_rvhBoLAACDwMAApnYkEU_b4TvaFm6-B4E", + "g_2": "CAACAgEAAxkDAAEDe7pllA2pP4FIrMKVOnCgvgesr5hhOQACSAIAAruTkEXqutZPzXqcXx4E", + "g_3": "CAACAgEAAxkDAAEDe7tllA2r9sc-h0p5781yXUxmIjG30QACCwIAAqnskEWxoXpxAW_zch4E", + "g_4": "CAACAgEAAxkDAAEDe7xllA2tI7jBpElvA-v9_y11syRh5AACQQMAAkh4kUWGlbmFagXAQB4E", + "g_5": "CAACAgEAAxkDAAEDe71llA2vGXcCAxbTfq3quZMhjtfQHgACbgIAApcckUVhphMsBYqfJx4E", + "g_6": "CAACAgEAAxkDAAEDe75llA2xHxOm-TiqMLml4YfVgIGHugACPgIAAi_OkEUiL81ENcLfSx4E", + "g_7": "CAACAgEAAxkDAAEDe79llA2yzUGRwz_nGdHVmyYKXdtUIAACDQMAAhSVkEVK2TL696mHEh4E", + "g_8": "CAACAgEAAxkDAAEDe8BllA20x6V1fiYfPK0AAZubQti3x90AAgkCAAI68ZFFxalNVgrhumweBA", + "g_9": "CAACAgEAAxkDAAEDe8FllA22AtfyKfEy2D1TD9yBg_vjhwAC8gIAAvp_kEUHMIge7TYyKB4E", + "g_draw": "CAACAgEAAxkDAAEDe8JllA24zAKP_EPj3WxQxWoCPY4edAACRAIAAujlkEXQU7jwZxRZiB4E", + "g_skip": "CAACAgEAAxkBAAIM12ZfWhoZlohTFinVWlZsgdV5CCEkAAITAgACuh2QRcLStZTh5J7VHgQ", + "g_reverse": "CAACAgEAAxkDAAEDe8RllA29Dre8XGZlqa4AAfSEXjS9LQYAAqsCAAKkLJFFiweqZ2bqu1QeBA", + "r_0": "CAACAgEAAxkDAAEDe8VllA3B-RkLpk1nq0jdcwy9BoSkHgACkAIAAloBkUX_0_LhZSJczR4E", + "r_1": "CAACAgEAAxkDAAEDe8ZllA3EVB2FIhJGiPIrtAACEeGVkgACggIAAuUMkUXYO2A7fOgafR4E", + "r_2": "CAACAgEAAxkDAAEDe8dllA3GdC6M4zzPTt8hxQNIQ7slzgACGAIAAv7xkEXl-k_R6oY76x4E", + "r_3": "CAACAgEAAxkDAAEDe8hllA3JRFrTn-sd5PRN4xWCM90tHwACGgMAAuC2kEXPCeJ41Blilx4E", + "r_4": "CAACAgEAAxkDAAEDe8lllA3MMqjbGbOk65ZW-iKojTrsFAAC5QIAAr8akUWeAsacvKVD7B4E", + "r_5": "CAACAgEAAxkDAAEDe8pllA3O9EyO50Rao1PvMn0fED0i7gAChwIAAhmTkEXI8f4hQtAPgB4E", + "r_6": "CAACAgEAAxkDAAEDe8tllA7PL1JfkjtApb9xfoFTDvDd2QACyAEAAnxpkUUXkvlBf7LZaR4E", + "r_7": "CAACAgEAAxkDAAEDe8xllA3TwEfjLkRJOk3bCYuMd_BhgAACtAEAAoGNkUW3IDX6hbLIRR4E", + "r_8": "CAACAgEAAxkDAAEDe81llA3Whl0dV_B5gN-LYVHHFFzJBAAC9wEAAl1lkEX1oBklhfbG7h4E", + "r_9": "CAACAgEAAxkDAAEDe85llA3ZKoQVIMWcAkgHEUebPu1BkwACpAIAAkG5kEWg5cauEqb-Eh4E", + "r_draw": "CAACAgEAAxkDAAEDe89llA3bEXr4bslsjfbgKJxX6_soWAACbAMAAiXikEUOrMwjMnq71h4E", + "r_skip": "CAACAgEAAxkDAAEDe9BllA3e3VxyE6B9kKt_o0uo3gO0QgAC_gIAAsVPkUXDxwuskYP9wB4E", + "r_reverse": "CAACAgEAAxkDAAEDe9FllA3h6JFRqSvdtHK7AzPGKhR93QAC3gIAAqXNkEVHBrFdkXLu8B4E", + "y_0": "CAACAgEAAxkDAAEDe9JllA3jVjW8wkE9VFRUdbvukteHigACTgIAAsdskUUJCGkroV6nOx4E", + "y_1": "CAACAgEAAxkDAAEDe9NllA3mM3HzFiwpLbIZmn1y7og-ogAC6QEAApAEkEVBGUptZYFNtx4E", + "y_2": "CAACAgEAAxkDAAEDe9RllA3pSe6Pq6XfdXrGSUwO_XnNRwAC9QIAAumPkUWO0RtAwk-gqh4E", + "y_3": "CAACAgEAAxkDAAEDe9ZllA3yb8nE9KLHDwf4TpLVSQi8PwACKAMAAlZkkEX3DMzHkfKt4x4E", + "y_4": "CAACAgEAAxkDAAEDe9dllA31GtSoqcIH83bG6E_-e_LMGwACcwIAAvhqkUVHXHrGg4bzkB4E", + "y_5": "CAACAgEAAxkDAAEDe9hllA34eX3HlQVCB3yz4jE_hxkcPgACvAIAAlrSkUXmuFcRjj_Ykh4E", + "y_6": "CAACAgEAAxkDAAEDe9lllA38BYT4tiz3UynZ1KlgBeE9zwACjAIAAucGkEUklwtxCimr8h4E", + "y_7": "CAACAgEAAxkDAAEDe9pllA3_D9P4Z8gbdSrJolueNEReHgACpAIAAmLqkEW8I1ITQ-K4UR4E", + "y_8": "CAACAgEAAxkDAAEDe9tllA4CfpWusm9qPlMzZB4d1OZuOwACDQIAAl0VkEUjRoTZaTeIiR4E", + "y_9": "CAACAgEAAxkDAAEDe9xllA4F1NkFzxkbrUN-tdzZP5X7HwACtwIAAleokUWrFCx6aDoY1B4E", + "y_draw": "CAACAgEAAxkDAAEDe91llA4IQ-kifRI4cFbGbMZBA3rWvwACVAIAAmnbkUWKjhkSgboWox4E", + "y_skip": "CAACAgEAAxkDAAEDe95llA4MjLGS0nj2fCILrVq0VDOu9AAC4gEAAsUYkUVNLlmImNML9h4E", + "y_reverse": "CAACAgEAAxkDAAEDe99llA4QK1OuIZxHXrjpHa72GHwnQwACxAIAAn1JkEUTDAHVfy579h4E", + "draw_four": "CAACAgEAAxkDAAEDd9tldPH_lqQbjb_fguXSJiUEZmXMDgACCAIAAmfQkEWdQcu_mD_LxR4E", + "colorchooser": "CAACAgEAAxkDAAEDd9xldPICMlwKe0ZQLmNenTD1fQ3cNQACaAIAAgeNkUXMnabDbsOjOh4E", + "mirror": "CAACAgEAAxkBAAIJjGWXDOVcBxySviDWuqpeCBt2wzsRAALfAgACM5iRRbhn4u7C82kqHgQ", + "option_draw": "CAACAgEAAxkDAAEDeJpldQABh5BKyV9yoabNg_K5EBnWbAkAAtYEAAL1-6hHbSd5q2HZFrAeBA", + "option_pass": "CAACAgQAAxkDAAI4JmNtY_yMlr6rB3UdTikR3zFCk8kVAAL6AgACX1eZAAFuilR5QnD-VysE", + "option_bluff": "CAACAgQAAxkDAAI4J2NtY_2Dmt5Mi4iZhsUh32OeNVe7AALKAgACX1eZAAHBw478rNqN0CsE", + "option_info": "CAACAgQAAxkDAAI4KGNtY_3tO0Sxhu5NzF1UA3tdUnklAALEAgACX1eZAAGi2Qy93IIQwisE" + }, + "STICKERS_GREY": { + "b_0": "CAACAgEAAxkDAAEDeBhldPQpr_HP5jMfg5eqmF9WwcbANwACzAEAAvMQkUW3xk9Rm7wDdB4E", + "b_1": "CAACAgEAAxkDAAEDeBlldPQtK30FApRgW8VPDinDZiBcMwACHAMAAhT5kEU5hLA1PG5mNx4E", + "b_2": "CAACAgEAAxkDAAEDeBpldPQy489pxZnGU_OJivHLZUz_rgAC8gIAAhWNkUUj2qPu53BQAR4E", + "b_3": "CAACAgEAAxkDAAEDeBtldPQ1QGHrATJf8y6xY1WOESH19wACywIAAoCvkEUusWPJE3SO8R4E", + "b_4": "CAACAgEAAxkDAAEDeBxldPQ4gNTK8wbbKPySKhH9HRQjvwACKAIAApa_kEU9N3ivQr1-1x4E", + "b_5": "CAACAgEAAxkDAAEDeB1ldPQ7La8udS3TXWrlx0X1-hfj-gACRwIAArP5kEVCQ9gkbL8Byh4E", + "b_6": "CAACAgEAAxkDAAEDeB5ldPQ-GnWfnbES5XLjjeEnCCx9QgACKQMAAqPkkUUugGSOjf9txR4E", + "b_7": "CAACAgEAAxkDAAEDeB9ldPRC1btFrtNIrvF2r5Cm46WbnQACWQIAAomUmUUvhGYvR8jwPh4E", + "b_8": "CAACAgEAAxkDAAEDeCBldPRFe3GVn8NG-kFW-6kCBfbZTwACaAIAAgg5kUUsEUnkv0FafR4E", + "b_9": "CAACAgEAAxkDAAEDeCFldPRIRXMxVF6-2SIuN9N2TcYSbwACSgwAAr8jkEXx2Ncg-cKr-B4E", + "b_draw": "CAACAgEAAxkDAAEDeCJldPRM9VR0-q4Nr1CH71nRpi3_-AACZQMAAtvQkUWdG5DeUdyYbR4E", + "b_skip": "CAACAgEAAxkDAAEDeCNldPRQkadczfkJ1AGbpGhP4hJoYQAClwMAAgQSkEVJkgABSqHf3RMeBA", + "b_reverse": "CAACAgEAAxkDAAEDeCRldPRSBY2Xe8gqahJ5uX7jhk72lgACXgIAArfzkUVdqU8Skpie5B4E", + "g_0": "CAACAgEAAxkDAAEDeCVldPRWthIVrMEDrpNDQkWocduO_AACcgIAAkpskEV35BNxHr8V5x4E", + "g_1": "CAACAgEAAxkDAAEDeCZldPRantCOaUJ5thTqgnk1aiL5QgACCwMAAs3ukUVOx82xQv9Ffx4E", + "g_2": "CAACAgEAAxkDAAEDeCdldPRdO7pkiMb9Jiiy5470Z8rUzAACewIAApPKkEUHSvig3w4D6R4E", + "g_3": "CAACAgEAAxkDAAEDeChldPRh9Wyk3_ttBrB4dKFW534KJQACNRYAAl_mkUUjJPhaXCrytR4E", + "g_4": "CAACAgEAAxkDAAEDeClldPRk_MvviiZl7flLtfzE_oJTLQAC2AIAAkIjkEU6INaWefsnbR4E", + "g_5": "CAACAgEAAxkDAAEDeCpldPRmgqPSrQI-xX4Y4tL9-ejIKQACSAMAAsXrkUU8dg1tlvXuIx4E", + "g_6": "CAACAgEAAxkDAAEDeCtldPRqS9IxSLuq2bC61gABw2TBEQADfgIAAtKEkEV2q3ae2QfTlR4E", + "g_7": "CAACAgEAAxkDAAEDeCxldPRuL0gfAcA0n2sx_f0iv5sctwACaQIAAvBfkEWujcXUoCoNkh4E", + "g_8": "CAACAgEAAxkDAAEDeDBldPTZGi65G5L8tGWUO3wOSixVhwACYAMAAr7jkUW_EGWLU2VY3x4E", + "g_9": "CAACAgEAAxkDAAEDeDFldPTatuaW2a1roWs1yQ0CknzrFAACZAIAAofZkUV4qNUOIfMGfB4E", + "g_draw": "CAACAgEAAxkDAAEDeDJldPTbUcBqKAzvWXOb7hYO3ebsVAAC2QIAAojIkEWLotFWFUrd9B4E", + "g_skip": "CAACAgEAAxkDAAEDeDNldPTdh9PvZ_VZ8XIm9Km6bX2J_QACTQIAArBokEVwbQsgazaHHB4E", + "g_reverse": "CAACAgEAAxkDAAEDeDRldPTd9yIAAZ4Y6szNLr_KmTCgvCgAAu8CAAKLQJFFVS-Ee507SeQeBA", + "r_0": "CAACAgEAAxkDAAEDeDVldPTeA_iz6315Ync-7RV5bjg8lgACMgYAAqQtkEUfzWAEmEoSsx4E", + "r_1": "CAACAgEAAxkDAAEDeDZldPTfPZCyQY-NBUz4YHZKVqkFdQACvgIAAhRRkUXPAAFc1XcuDSseBA", + "r_2": "CAACAgEAAxkDAAEDeDdldPTf3s_igw3SQLjEpVeg7cxa2AAC8wIAAn5ukEXFPt-unsEYFB4E", + "r_3": "CAACAgEAAxkDAAEDeDhldPTgnLoEQVmm8PXRZNjfcKT08wACHQMAAg0HkEUTAAEuHgIFMMgeBA", + "r_4": "CAACAgEAAxkDAAEDeDlldPTh80oMZu94S4aEgOHe_ETbzwACMgIAAloEkEU4V1zqccv4px4E", + "r_5": "CAACAgEAAxkBAAINHGZfWnsZDbEzrk-2D3HjMV1F65AAA6ACAAI7OJFFF9QnY0HDd4YeBA", + "r_6": "CAACAgEAAxkDAAEDeDtldPTjto0-gDKrLfvtOnLR3yedfwAC8QEAAh0PkUWDDCnQ0yKJ4B4E", + "r_7": "CAACAgEAAxkDAAEDeDxldPTj_eXDrdZOTuAgDapmmCGopgACjAIAAnyFkUWR0nnKzFE9Ox4E", + "r_8": "CAACAgEAAxkDAAEDeD1ldPTksKpKl4svmqhZp754lHk_4AAC1QIAAnjkkEWe22LmFa-TXx4E", + "r_9": "CAACAgEAAxkDAAEDeD5ldPTkK9WuHM_JGtcqX82HXids2AACBQQAAlV2kEXV6etMTirPNh4E", + "r_draw": "CAACAgEAAxkDAAEDeD9ldPTl95GlyWxd13otkkbwgYxnuQACIQIAArN7kEXAyhLPjeuM0B4E", + "r_skip": "CAACAgEAAxkDAAEDeEBldPTmDSmE_hf8iqfvTNw_MhN-DgACjQIAAgFikUUbhvkdjEvL_B4E", + "r_reverse": "CAACAgEAAxkDAAEDeEFldPTm_FseoIs4TkycuysDW93ClgACiwIAAvKekEWH4bB4fK_JAh4E", + "y_0": "CAACAgEAAxkDAAEDeEJldPUSOSBzeDwybGll4Z8UKWuTzgACzQIAAlZRkEUaN3RtvlPkhx4E", + "y_1": "CAACAgEAAxkDAAEDeENldPUTdMKXIDNbaFklvDnV8eyHfQAC_QIAAmS8kUWe4Qhj7djTNx4E", + "y_2": "CAACAgEAAxkDAAEDeERldPUTCwm7oP44jKDQXw_0v0EHFwACDAMAAkYEkEXwjgrHAngn8h4E", + "y_3": "CAACAgEAAxkDAAEDeEVldPUUK3_GcOUCyoAz9QxD6m21tAAC9AMAAuu9kUVv0c8dISk1Ix4E", + "y_4": "CAACAgEAAxkDAAEDeEZldPUUxdlEgfu7gmJvazzHtiVjLQACbwQAAp9KkUWXChzRPGdXKh4E", + "y_5": "CAACAgEAAxkDAAEDeEdldPUVJRjFaF2gCyeb9FI5eLHJYwACFQMAAqTfkUWYvUcRvcNxhh4E", + "y_6": "CAACAgEAAxkDAAEDeEhldPUWzqtfWclTzKpnM9poibfNIQACpgIAAhZ3kEW9Xv1BRth-4B4E", + "y_7": "CAACAgEAAxkDAAEDeElldPUWLMeeIuoZ_vL46CgPfzDTxwAC7QEAApxekEUmO5PhRBZWdx4E", + "y_8": "CAACAgEAAxkDAAEDeEpldPUXs-BExZa_58xEDvMRbS4_nAACfgIAAmg-kUUoRbaAW_B4HR4E", + "y_9": "CAACAgEAAxkDAAEDeEtldPUYcpBSMEHnXZkFkK6mAyF49AACqwIAAtsVkEVRqf1U9QqfdB4E", + "y_draw": "CAACAgEAAxkDAAEDeExldPUYd5oe-E69-C2kjrolu2s64wACqwIAAk1xkUXw_oyRmWZrwx4E", + "y_skip": "CAACAgEAAxkDAAEDeE1ldPUZ57bOPol5KNgUnLGEsWOCRQACUAIAAq70mEW_IHyXZAkgxB4E", + "y_reverse": "CAACAgEAAxkDAAEDeE5ldPUZlq9iapYBJlTdZv0OJ1HjiQACzwIAAiozkEUGSQFnhRdepR4E", + "draw_four": "CAACAgEAAxkDAAEDeE9ldPUmQloxc6_ReBmEx6mUL1GYTwACBAIAAisrmUUd5e1D0lszKB4E", + "colorchooser": "CAACAgEAAxkDAAEDeFBldPUxrSNyn9WqB7y9yp7QTeyTaAACDQIAAlZtkEUPHKKL4f24nR4E", + "mirror": "CAACAgEAAxkDAAEDeLpldRDJK0FwP0BId9Tnvf_2yBrz7AACBgIAAtSKkUWN6yeT8oqBeB4E" + }, + "CARDS": { + "SPECIALS": [ + "draw_four", + "colorchooser", + "mirror" + ], + "SPECIALS_INFO": { + "draw_four": [ + 4, + "." + ], + "colorchooser": [ + 4, + "^(?!.*draw).*$" + ], + "mirror": [ + 2, + "draw|skip" + ] + }, + "THEME_CARDS": [ + "mirror" + ], + "COLORS": [ + "b", + "g", + "r", + "y" + ], + "COLOR_ICONS": { + "b": "🟦", + "g": "🟩", + "r": "🟥", + "y": "🟨", + "x": "❓" + }, + "VALUES": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "draw", + "skip", + "reverse" + ], + "VALUES_ICONS": { + "0": "0️⃣", + "1": "1️⃣", + "2": "2️⃣", + "3": "3️⃣", + "4": "4️⃣", + "5": "5️⃣", + "6": "6️⃣", + "7": "7️⃣", + "8": "8️⃣", + "9": "9️⃣", + "draw": "+2", + "skip": "🚫", + "reverse": "🔁", + "colorchooser": "🌈", + "draw_four": "+4", + "mirror": "🪞" + } + } +} \ No newline at end of file diff --git a/config-example.py b/config-example.py new file mode 100644 index 0000000..c8ecdb0 --- /dev/null +++ b/config-example.py @@ -0,0 +1,12 @@ +from hydrogram import Client + + +games = {} +player_game = {} + +API_ID = '' +API_HASH = '' + +#--- Telegram config --- + +bot = Client("bot", api_id=API_ID, api_hash=API_HASH, plugins=dict(root="plugins")) diff --git a/db.py b/db.py new file mode 100644 index 0000000..220c4bf --- /dev/null +++ b/db.py @@ -0,0 +1,48 @@ +import os + +from tortoise import Tortoise, connections, fields +from tortoise.backends.base.client import Capabilities +from tortoise.models import Model + + +class Chat(Model): + id = fields.IntField(pk=True) + theme = fields.CharField(max_length=255, default="classic") + bluff = fields.BooleanField(default=True) + seven = fields.BooleanField(default=False) + one_win = fields.BooleanField(default=False) + one_card = fields.BooleanField(default=False) + lang = fields.CharField(max_length=255, default="en-US") + +class User(Model): + id = fields.BigIntField(pk=True) + placar = fields.BooleanField(default=False) + wins = fields.IntField(default=0) + matches = fields.IntField(default=0) + cards = fields.IntField(default=0) + sudo = fields.BooleanField(default=False) + lang = fields.CharField(max_length=255, default="en-US") + + +async def connect_database(): + await Tortoise.init( + { + "connections": { + "bot_db": os.getenv("DATABASE_URL", "sqlite://database.sqlite") + }, + "apps": {"bot": {"models": [__name__], "default_connection": "bot_db"}}, + } + ) + + conn = connections.get("bot_db") + conn.capabilities = Capabilities( + "sqlite", + daemon=False, + requires_limit=True, + inline_comment=True, + support_for_update=False, + support_update_limit_order_by=False, + ) + + # Generate the schema + await Tortoise.generate_schemas() diff --git a/deck.py b/deck.py new file mode 100644 index 0000000..fa5fb6b --- /dev/null +++ b/deck.py @@ -0,0 +1,23 @@ +import random +from card import cards + + +class Deck: + def __init__(self, theme) -> None: + self.cards = [ + (color, value) + for _ in range(2) + for color in cards[theme]["CARDS"]["COLORS"] + for value in cards[theme]["CARDS"]["VALUES"] + ] + self.cards += [ + ("x", car) + for car in cards[theme]["CARDS"]["SPECIALS"] + for _ in range(cards[theme]["CARDS"]["SPECIALS_INFO"][car][0]) + ] + + def shuffle(self): + random.shuffle(self.cards) + + def draw(self, amount): + return [self.cards.pop(0) for _ in range(amount) if self.cards] diff --git a/game.py b/game.py new file mode 100644 index 0000000..0f5e717 --- /dev/null +++ b/game.py @@ -0,0 +1,24 @@ +from hydrogram.types import Chat, User +from deck import Deck + + +class Game: + def __init__(self, chat: Chat, theme) -> None: + self.chat = chat + self.last_card = None + self.last_card_2 = None + self.next_player:User = None + self.deck = Deck(theme) + self.players = {} + self.is_started = False + self.draw = 0 + self.chosen = None + self.closed = False + self.winner = True + + def next(self): + if self.draw >= 0: + indice = list(self.players.keys()).index(self.next_player.id) + next_ind = (indice + 1) % len(self.players) + next_key = list(self.players.keys())[next_ind] + self.next_player = self.players[next_key] diff --git a/locales.py b/locales.py new file mode 100644 index 0000000..c5afb0a --- /dev/null +++ b/locales.py @@ -0,0 +1,93 @@ +import os +from functools import partial, wraps +from glob import glob +from typing import Dict, List +from hydrogram.types import Message +from hydrogram.enums import ChatType +from config import player_game +from game import Game + +import yaml + +from db import User, Chat + +langs = ["en-US", "pt-BR"] + +default_language = "en-US" + + +def cache_localizations(files: List[str]) -> Dict[str, Dict[str, Dict[str, str]]]: + ldict = {lang: {} for lang in langs} + for file in files: + _, pname = file.split(os.path.sep) + lang = pname.split(".")[0] + with open(file, "r") as f: + ldict[lang] = yaml.safe_load(f) + return ldict + + +jsons = [] + +for locale in langs: + jsons += glob(os.path.join("locales", f"{locale}.yml")) + +langdict = cache_localizations(jsons) + + +def use_lang(): + def decorator(func): + @wraps(func) + async def wrapper(client, message): + ulang = (await User.get_or_create(id=message.from_user.id))[0].lang + if not isinstance(message, Message): + if message.from_user.id not in player_game: + clang = ulang + else: + game: Game = player_game[message.from_user.id] + chatid = game.chat.id + clang = (await Chat.get_or_create(id=chatid))[0].lang + else: + clang = (await Chat.get_or_create(id=message.chat.id))[0].lang + ulfunc = partial(get_locale_string, ulang) + clfunc = partial(get_locale_string, clang) + return await func(client, message, ulfunc, clfunc) + + return wrapper + + return decorator + + +def use_chat_lang(): + def decorator(func): + @wraps(func) + async def wrapper(client, message): + mmessage = message.message if not isinstance(message, Message) else message + if mmessage.chat.type == ChatType.PRIVATE: + clang = (await User.get_or_create(id=mmessage.chat.id))[0].lang + else: + clang = (await Chat.get_or_create(id=mmessage.chat.id))[0].lang + clfunc = partial(get_locale_string, clang) + return await func(client, message, clfunc) + + return wrapper + + return decorator + + +def use_user_lang(): + def decorator(func): + @wraps(func) + async def wrapper(client, message): + ulang = (await User.get_or_create(id=message.from_user.id))[0].lang + ulfunc = partial(get_locale_string, ulang) + return await func(client, message, ulfunc) + + return wrapper + + return decorator + + +def get_locale_string(language: str, key: str) -> str: + print(f"Getting {key} for {language}") + res: str = langdict[language].get(key) or key + return res diff --git a/locales/en-US.yml b/locales/en-US.yml new file mode 100644 index 0000000..6373c01 --- /dev/null +++ b/locales/en-US.yml @@ -0,0 +1,69 @@ +lang_name: English +lang_flag: 🇺🇸 +lang_changed: The language has been changed to English! +back: Back +theme: Theme +info_theme: You choose a theme for the cards +seven_zero: 7-0 +info_seven: When a 7 is played, the player can swap cards with another player. When a 0 is played, everyone passes their cards to the next player. +sbluff: Bluff +info_bluff: When a player plays a +4 card, the next one can challenge. If the player who played the +4 card has another card to play, they draw 4 cards. If not, the one who challenged draws 6. +one_win: One winner +info_one_win: When the first player runs out of cards, the game ends and they are the winner. +one_card: Say UNO +info_one_card: When a player has only one card, they must say UNO. If they don't, they must draw 2 cards. +language: Language +info_lang: You can change the bot's language. +choose_lang: Choose a language +start_text: "Hello! I am the UNO Bot, a bot to play UNO on Telegram!\n\nTo play, put me in a group and send /new to start a new game!\n\nor to configure use the buttons below!" +game_mode: Game Mode +status: Status +game_mode_text: "Showing information of available game modes!\n\n" +settings: "Welcome to the settings menu!\n\nChoose an option below to configure the bot." +theme_config: "Choose a theme for the cards:" +minimalist: Minimalist +classic: Classic +colorblind: Colorblind +game_existis: There is already a game in progress! +only_group: This command only works in groups! +already_in_game: You are already in another game +already_joined: You are already in the game! +join: Join +leave: Leave +start_game: Start Game +game_started: A new game has started! +no_game: There is no game in progress! +no_game_text: No game in progress, send /new to start a new game! +lobby_closed: The lobby is closed! +lobby_opened: The lobby is open! +joined_game: You joined the game! +player_joined: "{} joined the game!" +no_joinned: You are not in the game! +next: Next {} +game_over: The game is over! +player_left: "{} left the game!" +left_game: You left the game! +not_allowed: You are not allowed to do that! +game_started: The game has already started! +info_text: 🃏 Game Information:\nCurrent player - {current_player}\nLast card - {last_card}\n +info_text2: "{player}: {cards} cards, " +pass: Pass +buy: Buy {} card(s) +bluff: I think you're bluffing! +cards_swapped: "{name1} swapped cards with {name2}" +not_swapped: Due to the number of players, it was not possible to swap cards! +swapped: The cards have been swapped! +bought: You bought {buy} cards! +bluffed: "{name} You can't bluff!\n{name} drew {draw} cards!" +not_bluffed: "{name1} You didn't bluff!\n{name2} drew {draw} cards!" +colorchoose: "{name} choose a color!" +colorchoosed: I chose the color {color}! +playerchoose: "{name} choose a player!" +invalid_card: Invalid move! +said_uno: "{name} shouted UNO!" +say_uno: "{name} say UNO or draw 2 cards!" +not_said_uno: "{name} You didn't shout UNO!\n{name} drew 2 cards!" +first: " in first" +won: "{name} won the game{comp}!" +continuing: continuing the game with {count} players +play: Play diff --git a/locales/pt-BR.yml b/locales/pt-BR.yml new file mode 100644 index 0000000..380a353 --- /dev/null +++ b/locales/pt-BR.yml @@ -0,0 +1,69 @@ +lang_name: Português +lang_flag: 🇧🇷 +lang_changed: O idioma foi alterado para Português! +back: Voltar +theme: Tema +info_theme: Você escolhe um tema para as cartas +seven_zero: 7-0 +info_seven: Quando um 7 é jogado, o jogador pode trocar as cartas com outro jogador. Quando um 0 é jogado, todos passam as cartas para o próximo jogador. +sbluff: Blefe +info_bluff: Quando um jogador joga uma carta +4, o próximo pode desafiar. Se o jogador que jogou a carta +4 tiver outra carta para jogar, ele compra 4 cartas. Se não tiver, quem desafiou compra 6. +one_win: Um vencedor +info_one_win: Quando o primeiro jogador fica sem cartas, o jogo acaba e ele é o vencedor. +one_card: Dizer UNO +info_one_card: Quando um jogador tem apenas uma carta, ele deve dizer UNO. Se ele não disser, ele deve comprar 2 cartas. +language: Idioma +info_lang: Você pode alterar o idioma do bot. +choose_lang: Escolha um idioma +start_text: "Olá! Eu sou o UNO Bot, um bot para jogar UNO no Telegram!\n\nPara jogar me coloque em um grupo e mande /new para começar um novo jogo!\n\nou para configurar use os botões abaixo!" +game_mode: Modo de Jogo +status: Status +game_mode_text: "Mostrando informações dos modos de jogo disponíveis!\n\n" +settings: "Bem vindo ao menu de configurações!\n\nEscolha uma opçãoe abaixo para configurar o bot." +theme_config: "Escolha um tema para as cartas:" +minimalist: Minimalista +classic: Clássico +colorblind: Daltônico +game_existis: Já existe um jogo em andamento! +only_group: Esse comando só funciona em grupos! +already_in_game: Você já está em outro jogo +already_joined: Você já está no jogo! +join: Entrar +leave: Sair +start_game: Começar Jogo +game_started: Um novo jogo foi iniciado! +no_game: Não há jogo em andamento! +no_game_text: Nenhum jogo em andamento, mande /new para iniciar um novo jogo! +lobby_closed: O lobby está fechado! +lobby_opened: O lobby está aberto! +joined_game: Você entrou no jogo! +player_joined: "{} entrou no jogo!" +no_joinned: Você não está no jogo! +next: Próximo {} +game_over: O jogo acabou! +player_left: "{} saiu do jogo!" +left_game: Você saiu do jogo! +not_allowed: Você não tem permissão para fazer isso! +game_started: O jogo já começou! +info_text: 🃏 Informações do Jogo:\nJogador atual - {current_player}\nÚltima carta - {last_card}\n +info_text2: "{player}: {cards} cartas, " +pass: Passar +buy: Comprar {} carta(s) +bluff: Eu acho que você está blefando! +cards_swapped: "{name1} trocou as cartas com {name2}" +not_swapped: Devido ao numero de jogadores, não foi possível trocar as cartas! +swapped: As cartas foram trocadas! +bought: Você comprou {buy} cartas! +bluffed: "{name} Você não pode blefar!\n{name} comprou {draw} cartas!" +not_bluffed: "{name1} Você não blefou!\n{name2} comprou {draw} cartas!" +colorchoose: "{name} escolha uma cor!" +colorchoosed: Eu escolhi a cor {color}! +playerchoose: "{name} escolha um jogador!" +invalid_card: Jogada inválida! +said_uno: "{name} gritou UNO!" +say_uno: "{name} fale UNO ou compre 2 cartas!" +not_said_uno: "{name} Você não gritou UNO!\n{name} comprou 2 cartas!" +first: " em primeiro" +won: "{name} ganhou o jogo{comp}!" +continuing: continuando o jogo com {count} jogadores +play: Jogar \ No newline at end of file diff --git a/plugins/game.py b/plugins/game.py new file mode 100644 index 0000000..dae8333 --- /dev/null +++ b/plugins/game.py @@ -0,0 +1,607 @@ +import importlib +import re + +from hydrogram import Client, filters +from hydrogram.enums import ChatType +from hydrogram.types import ( + ChosenInlineResult, + InlineKeyboardButton, + InlineKeyboardMarkup, + InlineQueryResultArticle, + InlineQueryResultCachedSticker, + InputTextMessageContent, + Message, + InlineQuery, + CallbackQuery, +) +from hydrogram.errors import ListenerTimeout + +from card import COLORS, cards +from config import games, player_game +from db import Chat, User +from game import Game +from typing import Union +from locales import use_lang + + +@Client.on_message(filters.command("new")) +@use_lang() +async def new_game(c: Client, m: Message, ut, ct): + await Chat.get_or_create(id=m.chat.id) + if ( + m.chat.id in games + or m.chat.type == ChatType.PRIVATE + or player_game.get(m.from_user.id) + ): + return await m.reply_text( + ut("game_existis") + if m.chat.id in games + else ut("only_group") + if m.chat.type == ChatType.PRIVATE + else ut("already_in_game") + ) + + game = Game(m.chat, (await Chat.get(id=m.chat.id)).theme) + game.players[m.from_user.id] = m.from_user + games[m.chat.id] = game + player_game[m.from_user.id] = game + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(ct("join"), callback_data="join_game"), + InlineKeyboardButton(ct("leave"), callback_data="leave_game"), + ], + [InlineKeyboardButton(ct("start_game"), callback_data="start_game")], + ] + ) + return await m.reply_text(ct("game_started"), reply_markup=keyb) + + +@Client.on_message(filters.command("join")) +@Client.on_callback_query(filters.regex("^join_game$")) +@use_lang() +async def join_game(c: Client, m: Union[Message, CallbackQuery], ut, ct): + if isinstance(m, CallbackQuery): + func = m.answer + else: + func = m.reply_text + game: Game = games.get(m.chat.id if isinstance(m, Message) else m.message.chat.id) + if not game or m.from_user.id in game.players or player_game.get(m.from_user.id): + return await func( + ut("no_game") + if not game + else ut("already_joined") + if m.from_user.id in game.players + else ut('already_in_game') + ) + elif game.closed: + return await func(ct("lobby_closed")) + + game.players[m.from_user.id] = m.from_user + player_game[m.from_user.id] = game + await func(ut("joined_game")) + if isinstance(m, CallbackQuery): + await c.send_message( + m.message.chat.id, ct("player_joined").format(m.from_user.mention) + ) + + if game.is_started: + game.players[m.from_user.id].cards = game.deck.draw(7) + game.players[m.from_user.id].total_cards = 0 + + +@Client.on_callback_query(filters.regex("^leave_game$")) +@Client.on_message(filters.command("leave")) +@use_lang() +async def leave_game(c: Client, m: Union[Message, CallbackQuery], ut, ct): + if isinstance(m, CallbackQuery): + func = m.answer + else: + func = m.reply_text + game: Game = games.get(m.chat.id if isinstance(m, Message) else m.message.chat.id) + if not game or m.from_user.id not in game.players: + return await func( + ut("no_game") if not game else ut("no_joinned") + ) + + if game.is_started and game.next_player.id == m.from_user.id: + inline_keyb = InlineKeyboardMarkup( + [[InlineKeyboardButton(ct("play"), switch_inline_query_current_chat="")]] + ) + game.next() + await c.send_message( + game.chat.id, ct("next").format(game.next_player.mention), reply_markup=inline_keyb + ) + del game.players[m.from_user.id] + del player_game[m.from_user.id] + chat_id = m.chat.id if isinstance(m, Message) else m.message.chat.id + + if len(game.players) == 0: + games.pop(m.chat.id if isinstance(m, Message) else m.message.chat.id) + if isinstance(m, CallbackQuery): + await c.send_message( + chat_id=chat_id, + text=ct("player_left").format(m.from_user.mention)+", "+ct("game_over"), + ) + return await func(ut("game_over")) + if isinstance(m, CallbackQuery): + await c.send_message( + chat_id=chat_id, text=ct("player_left").format(m.from_user.mention) + ) + return await func(ut("left_game")) + + +@Client.on_message(filters.command("close")) +@use_lang() +async def close_game(c: Client, m: Message, ut, ct): + game = games.get(m.chat.id) + if not game or m.from_user != list(game.players.values())[0]: + return await m.reply_text( + ut("no_game") + if not game + else ut("not_allowed") + ) + + games.closed = True + return await m.reply_text(ct("lobby_closed")) + + +@Client.on_message(filters.command("open")) +@use_lang() +async def open_game(c: Client, m: Message, ut, ct): + """Handles the opening of a game.""" + game = games.get(m.chat.id) + if not game or m.from_user != list(game.players.values())[0]: + return await m.reply_text( + ut("no_game") + if not game + else ut("not_allowed") + ) + + games.closed = False + return await m.reply_text(ct("lobby_opened")) + + +@Client.on_message(filters.command("kill")) +@use_lang() +async def kill_game(c: Client, m: Message, ut, ct): + game = games.get(m.chat.id) + if not game or m.from_user != list(game.players.values())[0]: + return await m.reply_text( + ut("no_game") + if not game + else ut("not_allowed") + ) + + games.pop(m.chat.id) + for player in game.players: + player_game.pop(player) + return await m.reply_text(ct("game_over")) + + +@Client.on_message(filters.command("start") & ~filters.private) +@Client.on_callback_query(filters.regex("^start_game$")) +@use_lang() +async def start_game(c: Client, m: Union[Message, CallbackQuery], ut, ct): + if isinstance(m, CallbackQuery): + func = m.answer + chat_id = m.message.chat.id + else: + func = m.reply_text + chat_id = m.chat.id + config = await Chat.get(id=chat_id) + theme = config.theme + game = games.get(chat_id) + if not game or m.from_user != list(game.players.values())[0]: + return await func( + ut("no_game") + if not game + else ut("not_allowed") + ) + + game.is_started = True + game.deck.shuffle() + for player in game.players.values(): + player.cards = game.deck.draw(7) + player.total_cards = 0 + + await c.send_message(chat_id=chat_id, text=ct("game_started")) + pcard = next( + ( + lcard + for lcard in game.deck.cards + if lcard[1] not in cards[config.theme]["CARDS"]["SPECIALS"] + ), + None, + ) + game.deck.cards.remove(pcard) + game.deck.cards.append(pcard) + if pcard[1] == "draw": + if game.draw == -1: + game.draw = 0 + game.draw += 2 + await c.send_sticker( + chat_id=chat_id, sticker=cards[theme]["STICKERS"][f"{pcard[0]}_{pcard[1]}"] + ) + game.last_card = pcard + game.next_player = list(game.players.values())[0] + inline_keyb = InlineKeyboardMarkup( + [[InlineKeyboardButton(ct("play"), switch_inline_query_current_chat="")]] + ) + return await c.send_message( + chat_id, + ct("next").format(game.next_player.mention), + reply_markup=inline_keyb, + ) + + +@Client.on_inline_query(group=3) +@use_lang() +async def inline_query(c: Client, m: InlineQuery, ut, ct): + game = player_game.get(m.from_user.id) + if not game: + articles = [ + InlineQueryResultArticle( + id="none", + title=ut("no_game"), + input_message_content=InputTextMessageContent( + ut("no_game_text") + ), + ) + ] + return await m.answer(articles, cache_time=0) + + config = await Chat.get(id=game.chat.id) + theme = config.theme + COLOR_ICONS = cards[theme]["CARDS"]["COLOR_ICONS"] + VALUES_ICONS = cards[theme]["CARDS"]["VALUES_ICONS"] + if game.chosen == "color" and game.next_player.id == m.from_user.id: + pre = "" + if game.last_card[1] in cards[theme]["CARDS"]["THEME_CARDS"]: + pre = f"{game.last_card[1]}-" + articles = [ + InlineQueryResultArticle( + id=pre + color, + title=COLOR_ICONS[color], + input_message_content=InputTextMessageContent( + ct("colorchoosed").format(color=COLOR_ICONS[color]) + ), + ) + for color in COLORS + ] + await m.answer(articles, cache_time=0) + return + elif game.chosen == "player" and game.next_player.id == m.from_user.id: + players = list(game.players.keys()) + players.remove(m.from_user.id) + articles = [] + pre = "" + if game.last_card[1] in cards[theme]["CARDS"]["THEME_CARDS"]: + pre = f"{game.last_card[1]}-" + for player in players: + if len(players) == 1 or player != m.from_user.id: + articles.append( + InlineQueryResultArticle( + id=pre + str(player), + title=game.players[player].first_name, + input_message_content=InputTextMessageContent( + game.players[player].mention + ), + ) + ) + await m.answer(articles, cache_time=0) + return + + info_text = ut("info_text").format(current_player=game.next_player.mention, last_card=COLOR_ICONS[game.last_card[0]]+VALUES_ICONS[game.last_card[1]]) + for fplayer in game.players: + info_text += ut("info_text2").format(player=game.players[fplayer].mention, cards=len(game.players[fplayer].cards)) + + if not game or m.from_user.id != game.next_player.id: + articles = [] + for num, pcard in enumerate(game.players[m.from_user.id].cards): + sticker_type = pcard[1] if pcard[0] == "x" else f"{pcard[0]}_{pcard[1]}" + articles += [ + InlineQueryResultCachedSticker( + id=f"info-{num}", + sticker_file_id=cards[theme]["STICKERS_GREY"][sticker_type], + input_message_content=InputTextMessageContent(info_text), + ) + ] + + return await m.answer(articles, cache_time=0, is_gallery=True) + + gcards = game.players[m.from_user.id].cards + lcard = game.last_card if game.draw < 2 else ("p", "draw") + xcard = f"{lcard[0]}_{lcard[1]}" + sticker_id = "option_pass" if game.draw == -1 else "option_draw" + sticker_text = ( + ut("pass") + if game.draw == -1 + else ut("buy").format(1) + if game.draw == 0 + else ut("buy").format(game.draw) + ) + articles = [ + InlineQueryResultCachedSticker( + id=sticker_id, + sticker_file_id=cards[theme]["STICKERS"][sticker_id], + input_message_content=InputTextMessageContent(sticker_text), + ) + ] + + if game.last_card[1] == "draw_four" and game.draw == 4: + if (await Chat.get(id=game.chat.id)).bluff: + articles.append( + InlineQueryResultCachedSticker( + id="option_bluff", + sticker_file_id=cards[theme]["STICKERS"]["option_bluff"], + input_message_content=InputTextMessageContent( + ut("bluff") + ), + ) + ) + + for num, pcard in enumerate(gcards): + sticker_type = pcard[1] if pcard[0] == "x" else f"{pcard[0]}_{pcard[1]}" + if ( + ( + pcard[1] in cards[theme]["CARDS"]["SPECIALS_INFO"] + and re.search( + cards[theme]["CARDS"]["SPECIALS_INFO"][pcard[1]][1], string=xcard + ) + ) + or pcard[0] == lcard[0] + or pcard[1] == lcard[1] + ): + articles.append( + InlineQueryResultCachedSticker( + id=f"{pcard[0]}-{pcard[1]}-{num}", + sticker_file_id=cards[theme]["STICKERS"][sticker_type], + ) + ) + else: + articles.append( + InlineQueryResultCachedSticker( + id=f"info-{num}", + sticker_file_id=cards[theme]["STICKERS_GREY"][sticker_type], + input_message_content=InputTextMessageContent(info_text), + ) + ) + + await m.answer(articles, cache_time=0, is_gallery=True) + + +@Client.on_chosen_inline_result(group=1) +@use_lang() +async def choosen(c: Client, ir: ChosenInlineResult, ut, ct): + game: Game = player_game.get(ir.from_user.id) + if not game and game.next_player.id != ir.from_user.id: + return + + config = await Chat.get(id=game.chat.id) + pcard = ir.result_id.split("-")[:2] + ncard = ir.result_id.split("-")[2] if len(ir.result_id.split("-")) > 2 else None + lcard = game.last_card if game.draw <= 1 else ("y", "draw") + print(game.deck.cards) + inline_keyb = InlineKeyboardMarkup( + [[InlineKeyboardButton(ct("play"), switch_inline_query_current_chat="")]] + ) + if pcard[0] == "info": + return + elif game.chosen == "color": + game.last_card = (ir.result_id, game.last_card[1]) + game.chosen = None + elif game.chosen == "player" and game.last_card_2["card"][1] == "7": + game.players[ir.from_user.id].cards, game.players[int(pcard[0])].cards = ( + game.players[int(pcard[0])].cards, + game.players[ir.from_user.id].cards, + ) + game.chosen = None + await c.send_message( + game.chat.id, + ct("cards_swapped").format(name1=game.next_player.mention, name2=game.players[int(pcard[0])].mention), + ) + game.next() + pcard.append("player") + await verify_cards(game, c, ir, game.players[int(pcard[0])], ut, ct) + elif pcard[0] == "option_draw": + buy = game.draw if game.draw > 0 else 1 + game.players[ir.from_user.id].cards.extend(game.deck.draw(buy)) + await c.send_message(game.chat.id, ct("bought").format(buy=buy)) + game.draw = 0 if buy >= 2 else -1 if len(game.players) != 1 else 0 + elif pcard[0] == "option_pass": + game.draw = 0 + elif pcard[0] == "option_bluff": + lplayer = game.players[game.last_card_2["player"]] + bcard = game.last_card_2["card"] + if any(bcard[0] in tupla or bcard[1] in tupla for tupla in lplayer.cards): + lplayer.cards.extend(game.deck.draw(game.draw)) + await c.send_message( + game.chat.id, + ct("bluffed").format(name=lplayer.mention, draw=game.draw), + ) + game.draw = 0 + await c.send_message( + game.chat.id, + ct("next").format(game.next_player.mention), + reply_markup=inline_keyb, + ) + return + else: + game.players[ir.from_user.id].cards.extend(game.deck.draw(game.draw + 2)) + await c.send_message( + game.chat.id, + ct("not_bluffed").format(name1=lplayer.mention, name2=ir.from_user.mention, draw=game.draw + 2), + ) + game.draw = 0 + game.next() + await c.send_message( + game.chat.id, + f"Próximo: {game.next_player.mention}", + reply_markup=inline_keyb, + ) + return + elif pcard[0] == "x" and pcard[1] in ["colorchooser", "draw_four"]: + game.players[ir.from_user.id].total_cards += 1 + if pcard[1] == "draw_four": + if game.draw == -1: + game.draw = 0 + game.draw += 4 + game.last_card_2 = {"card": game.last_card, "player": ir.from_user.id} + game.last_card = game.players[ir.from_user.id].cards.pop(int(ncard)) + game.deck.cards.append(game.last_card) + game.chosen = "color" + return await c.send_message( + game.chat.id, + ct("colorchoose").format(name=game.next_player.mention), + reply_markup=inline_keyb, + ) + elif ( + pcard[0] in cards[config.theme]["CARDS"]["THEME_CARDS"] + or (pcard[1] and pcard[1] in cards[config.theme]["CARDS"]["THEME_CARDS"]) + ): + game.deck.cards.append(game.last_card) + module = importlib.import_module(f"spetials.{config.theme}") + function = getattr( + module, + pcard[1] + if pcard[1] in cards[config.theme]["CARDS"]["THEME_CARDS"] + else pcard[0], + ) + ret = await function(c, ir, game) + if ret: + return + elif pcard[0] != "x" and (pcard[1] == lcard[1] or pcard[0] == lcard[0]): + game.players[ir.from_user.id].total_cards += 1 + game.draw += 2 if pcard[1] == "draw" else 0 + game.last_card_2 = {"card": game.last_card, "player": ir.from_user.id} + game.last_card = game.players[ir.from_user.id].cards.pop(int(ncard)) + game.deck.cards.append(game.last_card) + if pcard[1] == "reverse": + game.players = { + k: game.players[k] for k in reversed(list(game.players.keys())) + } + game.next() if len(game.players) == 2 else None + elif pcard[1] == "skip": + game.next() + elif ((await Chat.get(id=game.chat.id)).seven) and ( + pcard[1] == "7" or pcard[1] == "0" + ): + if pcard[1] == "7": + if len(game.players) == 1 or len(game.players) == 2: + await c.send_message( + game.chat.id, + ct("not_swapped") + ) + else: + game.chosen = "player" + game.last_card_2 = { + "card": game.last_card, + "player": ir.from_user.id, + } + return await c.send_message( + game.chat.id, + ct("playerchoose").format(name=game.next_player.mention), + reply_markup=inline_keyb, + ) + else: + # Aqui irá trocar as cartas dos jogadores na ordem do jogo + gcards = {} + a = 0 + for i in game.players: + gcards[a] = game.players[i].cards + a += 1 + + a = 1 + for i in game.players: + if a in gcards: + game.players[i].cards = gcards[a] + else: + game.players[i].cards = gcards[0] + a += 1 + await verify_cards(game, c, ir, game.players[i], ut, ct) + + await c.send_message(game.chat.id, ct("swapped")) + if game.draw == -1: + game.draw = 0 + else: + return await c.send_message(game.chat.id, ct("invalid_card")) + + if games.get(game.chat.id): + await verify_cards(game, c, ir, ir.from_user, ut, ct) + game.next() + return await c.send_message( + game.chat.id, ct("next").format(game.next_player.mention), reply_markup=inline_keyb + ) + +async def verify_cards(game, c, ir, user, ut, t): + inline_keyb = InlineKeyboardMarkup( + [[InlineKeyboardButton(t("play"), switch_inline_query_current_chat="")]] + ) + if len(game.players[user.id].cards) == 1: + if not (await Chat.get(id=game.chat.id)).one_card: + await c.send_message(game.chat.id, t("said_uno").format(name=user.mention)) + else: + await c.send_message( + game.chat.id, + ut("say_uno").format(name=user.mention), + ) + uno = False + while True: + try: + cmessage = await game.chat.listen( + filters.text | filters.user(user.id), timeout=5 + ) + if (cmessage and cmessage.text) and "uno" in cmessage.text.lower(): + uno = True + break + except ListenerTimeout: + break + if uno: + await c.send_message( + game.chat.id, t("said_uno").format(name=user.mention) + ) + else: + game.players[user.id].cards.extend(game.deck.draw(2)) + await c.send_message( + game.chat.id, + t("not_said_uno").format(name=user.mention), + ) + + elif len(game.players[user.id].cards) == 0: + comp_text = t("first") if game.winner else "" + await c.send_message(game.chat.id, t("won").format(name=user.mention, comp=comp_text)) + if game.winner: + game.winner = False + db_user = await User.get_or_none(id=user.id) + if db_user and db_user.placar: + db_user.wins += 1 + await db_user.save() + if (await Chat.get(id=game.chat.id)).one_win and len(game.players) > 2: + db_user = await User.get_or_none(id=user.id) + if db_user and db_user.placar: + db_user.matches += 1 + db_user.cards += game.players[user.id].total_cards + await db_user.save() + game.next() + game.players.pop(user.id) + player_game.pop(user.id) + await c.send_message( + game.chat.id, t("continuing").format(count=len(game.players)) + ) + await c.send_message( + game.chat.id, + t("next").format(game.next_player.mention), + reply_markup=inline_keyb, + ) + else: + games.pop(game.chat.id) + for player in game.players: + db_user = await User.get_or_none(id=player) + if db_user and db_user.placar: + db_user.matches += 1 + db_user.cards += game.players[player].total_cards + await db_user.save() + player_game.pop(player) + await c.send_message(game.chat.id, t("game_over")) + return diff --git a/plugins/settings.py b/plugins/settings.py new file mode 100644 index 0000000..09ad5e9 --- /dev/null +++ b/plugins/settings.py @@ -0,0 +1,209 @@ +from hydrogram import Client, filters +from hydrogram.types import Message, CallbackQuery +from hydrogram.enums import ChatType, ChatMemberStatus +from hydrogram.types import InlineKeyboardMarkup, InlineKeyboardButton +from typing import Union +from functools import partial +from card import cards +from db import Chat, User +from config import games +from locales import langs, get_locale_string, use_chat_lang, use_user_lang +from hydrogram.helpers import ikb + + +@Client.on_callback_query(filters.regex("^settings$")) +@Client.on_message(filters.command("settings")) +@use_chat_lang() +async def settings(c: Client, m: Union[Message, CallbackQuery], t): + if ( + isinstance(m, Message) + and m.chat.type == ChatType.PRIVATE + or isinstance(m, CallbackQuery) + and m.message.chat.type == ChatType.PRIVATE + ): + print(m) + if isinstance(m, Message): + func = m.reply_text + else: + func = m.edit_message_text + + x = await User.get(id=m.from_user.id) + keyb = [ + [(t("language"), "info_lang"), (t("lang_flag"), "lang")], + [(t("status"), "status"), ("✅" if x.placar else "✖️", "ch_status")], + ] + + if await filters.filter_sudoers(c, m): + keyb.append([("sudos", "sudos")]) + + keyb.append([(t("back"), "start")]) + + await func(t("settings"), reply_markup=ikb(keyb)) + else: + chat_id = m.chat.id if isinstance(m, Message) else m.message.chat.id + admin = await c.get_chat_member(chat_id, m.from_user.id) + print(admin) + if admin.status == ChatMemberStatus.MEMBER: + await m.reply("You need to be an admin to change the settings!") + return + if games.get(chat_id) and games[chat_id].is_started: + await m.reply("You can't change the settings while a game is running!") + return + x = (await Chat.get_or_create(id=chat_id))[0] + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(t("theme"), callback_data="info_theme"), + InlineKeyboardButton(t(x.theme), callback_data="theme"), + ], + [ + InlineKeyboardButton(t("seven_zero"), callback_data="info_seven"), + InlineKeyboardButton( + "✅" if x.seven else "✖️", callback_data="mode_seven" + ), + ], + [ + InlineKeyboardButton(t("sbluff"), callback_data="info_bluff"), + InlineKeyboardButton( + "✅" if x.bluff else "✖️", callback_data="mode_bluff" + ), + ], + [ + InlineKeyboardButton(t("one_win"), callback_data="info_one_win"), + InlineKeyboardButton( + "✅" if x.one_win else "✖️", callback_data="mode_one_win" + ), + ], + [ + InlineKeyboardButton(t("one_card"), callback_data="info_one_card"), + InlineKeyboardButton( + "✅" if x.one_card else "✖️", callback_data="mode_one_card" + ), + ], + [ + InlineKeyboardButton(t("language"), callback_data="info_lang"), + InlineKeyboardButton(t("lang_flag"), callback_data="lang"), + ], + ] + ) + if isinstance(m, Message): + await m.reply_text(t("settings"), reply_markup=keyb) + else: + await m.edit_message_text(t("settings"), reply_markup=keyb) + + +@Client.on_callback_query(filters.regex("^theme")) +@use_chat_lang() +async def theme(c: Client, cq: CallbackQuery, t): + if " " in cq.data: + await Chat.get(id=cq.message.chat.id).update(theme=cq.data.split(" ")[1]) + keyb = InlineKeyboardMarkup( + [[InlineKeyboardButton(t("back"), callback_data="theme")]] + ) + await cq.message.edit_text("Theme changed!", reply_markup=keyb) + else: + themes = cards.keys() + tkeyb = [ + InlineKeyboardButton(text=t(theme), callback_data=f"theme {theme}") + for theme in themes + ] + keyb = InlineKeyboardMarkup( + [tkeyb, [InlineKeyboardButton(text=t("back"), callback_data="settings")]] + ) + await cq.message.edit_text(t("theme_config"), reply_markup=keyb) + + +@Client.on_callback_query(filters.regex("mode_")) +@use_chat_lang() +async def mode(c: Client, cq: CallbackQuery, t): + admin = await c.get_chat_member(cq.message.chat.id, cq.from_user.id) + if admin.status is ChatMemberStatus.MEMBER: + await cq.answer("You need to be an admin to change the settings!") + return + x = await Chat.get(id=cq.message.chat.id) + if cq.data == "mode_seven": + await Chat.get(id=cq.message.chat.id).update(seven=not x.seven) + elif cq.data == "mode_bluff": + await Chat.get(id=cq.message.chat.id).update(bluff=not x.bluff) + elif cq.data == "mode_one_win": + await Chat.get(id=cq.message.chat.id).update(one_win=not x.one_win) + elif cq.data == "mode_one_card": + await Chat.get(id=cq.message.chat.id).update(one_card=not x.one_card) + await cq.answer("Done!") + x = await Chat.get(id=cq.message.chat.id) + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(t("theme"), callback_data="info_theme"), + InlineKeyboardButton(t(x.theme), callback_data="theme"), + ], + [ + InlineKeyboardButton(t("seven_zero"), callback_data="info_seven"), + InlineKeyboardButton( + "✅" if x.seven else "✖️", callback_data="mode_seven" + ), + ], + [ + InlineKeyboardButton(t("sbluff"), callback_data="info_bluff"), + InlineKeyboardButton( + "✅" if x.bluff else "✖️", callback_data="mode_bluff" + ), + ], + [ + InlineKeyboardButton(t("one_win"), callback_data="info_one_win"), + InlineKeyboardButton( + "✅" if x.one_win else "✖️", callback_data="mode_one_win" + ), + ], + [ + InlineKeyboardButton(t("one_card"), callback_data="info_one_card"), + InlineKeyboardButton( + "✅" if x.one_card else "✖️", callback_data="mode_one_card" + ), + ], + [ + InlineKeyboardButton(t("language"), callback_data="info_lang"), + InlineKeyboardButton(t("lang_flag"), callback_data="lang"), + ], + ] + ) + await cq.message.edit_text(t("settings"), reply_markup=keyb) + + +@Client.on_callback_query(filters.regex("^lang")) +@use_chat_lang() +async def lang(c: Client, cq: CallbackQuery, t): + if cq.message.chat.type != ChatType.PRIVATE: + admin = await c.get_chat_member(cq.message.chat.id, cq.from_user.id) + if admin.status is ChatMemberStatus.MEMBER: + await cq.answer("You need to be an admin to change the theme!") + return + if "_" in cq.data: + nt = partial(get_locale_string, cq.data.split("_")[1]) + if cq.message.chat.type == ChatType.PRIVATE: + await User.get(id=cq.message.chat.id).update(lang=cq.data.split("_")[1]) + else: + await Chat.get(id=cq.message.chat.id).update(lang=cq.data.split("_")[1]) + keyb = InlineKeyboardMarkup( + [[InlineKeyboardButton(nt("back"), callback_data="settings")]] + ) + await cq.message.edit_text(nt("lang_changed"), reply_markup=keyb) + else: + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + get_locale_string(lang, "lang_name"), + callback_data=f"lang_{lang}", + ) + ] + for lang in langs + ] + ) + await cq.message.edit_text(t("choose_lang"), reply_markup=keyb) + + +@Client.on_callback_query(filters.regex("^info_")) +@use_user_lang() +async def info(c: Client, cq: CallbackQuery, t): + await cq.answer(t(cq.data), show_alert=True) diff --git a/plugins/start.py b/plugins/start.py new file mode 100644 index 0000000..4dfb28c --- /dev/null +++ b/plugins/start.py @@ -0,0 +1,68 @@ +from hydrogram import Client, filters +from hydrogram.types import Message, CallbackQuery +from hydrogram.helpers import ikb +from typing import Union +from db import User +from locales import use_user_lang + + +@Client.on_message(filters.command("start") & filters.private) +@Client.on_callback_query(filters.regex("^start$")) +@use_user_lang() +async def start(c: Client, m: Union[Message, CallbackQuery], t): + if isinstance(m, CallbackQuery): + func = m.edit_message_text + else: + func = m.reply_text + await User.get_or_create(id=m.from_user.id) + print(m.from_user.id) + keyb = [[("help", "help"), ("configurações", "settings")]] + await func(t("start_text"), reply_markup=ikb(keyb)) + + +@Client.on_callback_query(filters.regex("^help$")) +@use_user_lang() +async def help(c: Client, cq: CallbackQuery, t): + keyb = [ + [(t("game_mode"), "help_game")], + [(t("back"), "start")], + ] + await cq.edit_message_text( + "Ecolha uma opção de ajuda abaixo:", reply_markup=ikb(keyb) + ) + + +@Client.on_callback_query(filters.regex("^help_game$")) +@use_user_lang() +async def help_game(c: Client, cq: CallbackQuery, t): + text = t("game_mode_text") + text += "" + t("seven_zero") + ": " + t("info_seven") + "\n\n" + text += "" + t("sbluff") + ": " + t("info_bluff") + "\n\n" + text += "" + t("one_win") + ": " + t("info_one_win") + "\n\n" + text += "" + t("one_card") + ": " + t("info_one_card") + "\n\n" + keyb = [[(t("back"), "help")]] + + await cq.edit_message_text(text, reply_markup=ikb(keyb)) + + +@Client.on_message(filters.command("status")) +@Client.on_callback_query(filters.regex("^status$")) +async def status(c: Client, m: Union[Message, CallbackQuery]): + user = await User.get_or_create(id=m.from_user.id) + porcentagem = (user[0].wins / user[0].matches) * 100 if user[0].matches > 0 else 0 + if (await User.get(id=m.from_user.id)).placar: + text = f"{user[0].matches} partidas jogadas\n{user[0].wins} vezes em primeiro lugas ({porcentagem}%)\n{user[0].cards} cartas jogadas" + else: + text = "Ligue para saber suas estatísticas como quantas partidas jogadas, quantas vezes em primeiro lugar e quantas cartas jogadas" + if isinstance(m, Message): + await m.reply_text(text) + else: + await m.answer(text, show_alert=True) + +@Client.on_callback_query(filters.regex("^ch_status$")) +async def ch_status(c: Client, cq: CallbackQuery): + user = await User.get_or_create(id=cq.from_user.id) + user = user[0] + user.placar = not user.placar + await user.save() + await cq.answer("Status alterado com sucesso") diff --git a/plugins/sudos.py b/plugins/sudos.py new file mode 100644 index 0000000..b3f72cd --- /dev/null +++ b/plugins/sudos.py @@ -0,0 +1,467 @@ +from asyncio import sleep +from json import dump +from typing import Union + +from hydrogram import Client, filters +from hydrogram.errors import ListenerTimeout, MediaEmpty +from hydrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + InlineQuery, + InlineQueryResultCachedSticker, + Message, +) + +from card import cards +from config import sudoers +from db import User + + +async def filter_sudoers_logic(flt, c, u): + if not u.from_user: + return None + usr = u.from_user + db_usr = await User.get_or_none(id=usr.id) + if not db_usr: + return False + if db_usr.sudo or usr.id in sudoers: + return True + else: + return False + + +filter_sudoers = filters.create(filter_sudoers_logic, "FilterSudoers") +filters.filter_sudoers = filter_sudoers + + +@Client.on_message(filters.command("sudos") & filters.private & filter_sudoers) +@Client.on_callback_query(filters.regex("^sudos$") & filter_sudoers) +async def start(c: Client, m: Union[Message, CallbackQuery]): + if isinstance(m, CallbackQuery): + func = m.edit_message_text + else: + func = m.reply_text + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("Sudos", callback_data="settings_sudos"), + InlineKeyboardButton("themes", callback_data="settings_sudo_themc"), + ], + [ + InlineKeyboardButton("Voltar", callback_data="settings"), + ] + ] + ) + await func( + "Bem vindo ao menu de configurações do UnuRobot, aqui você pode configurar o bot da forma que quiser, para isso basta clicar em um dos botões abaixo!", + reply_markup=keyb, + ) + + +@Client.on_callback_query(filters.regex("^settings_sudos$") & filter_sudoers) +async def settings_sudos(c: Client, cq: CallbackQuery): + usrs = await c.get_users(sudoers) + db_usrs = await User.filter(sudo=True) + usrs += await c.get_users([usr.id for usr in db_usrs]) + text = "Lista de sudos:\n\n" + for usr in usrs: + text += f"👤 {usr.mention}\n" + + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("Adicionar", callback_data="settings_sudos_add"), + InlineKeyboardButton("Remover", callback_data="settings_sudos_remove"), + ], + [InlineKeyboardButton("Voltar", callback_data="sudos")], + ] + ) + + await cq.edit_message_text(text, reply_markup=keyb) + + +@Client.on_callback_query(filters.regex("^settings_sudos_add$") & filter_sudoers) +async def settings_sudos_add(c: Client, cq: CallbackQuery): + await cq.edit_message_text("Envie o ID do usuário que deseja adicionar aos sudos") + cmessage = None + # Wait for the user to send a message with the new emoji + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.text) + print(cmessage) + except ListenerTimeout: + return + + text = cmessage.text + user = await c.get_users(text) + + if not user: + await cmessage.reply_text("Usuário não encontrado") + return + + user_db = await User.get_or_create(id=user.id) + + if user_db[0].sudo: + await cmessage.reply_text("Usuário já é sudo") + return + + user_db[0].sudo = True + await user_db[0].save() + + await cmessage.reply_text( + "Usuário adicionado aos sudos", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Voltar", callback_data="settings_sudos")]] + ), + ) + + +@Client.on_callback_query(filters.regex("^settings_sudos_remove") & filter_sudoers) +async def settings_sudos_remove(c: Client, cq: CallbackQuery): + if cq.data == "settings_sudos_remove": + db_users = await User.filter(sudo=True) + users = await c.get_users([usr.id for usr in db_users]) + text = "Selecione o usuário que deseja remover dos sudos:\n\n" + keyb = [] + for user in users: + keyb.append( + [ + InlineKeyboardButton( + user.first_name, + callback_data=f"settings_sudos_remove_{user.id}", + ) + ] + ) + + keyb.append([InlineKeyboardButton("Voltar", callback_data="settings_sudos")]) + await cq.edit_message_text(text, reply_markup=InlineKeyboardMarkup(keyb)) + return + + db_users = await User.filter(sudo=True) + sudoers = [usr.id for usr in db_users] + + user_id = int(cq.data.split("_")[3]) + if user_id not in sudoers: + await cq.answer("Usuário não é sudo") + return + + await User.get(id=user_id).update(sudo=False) + + await cq.edit_message_text( + "Usuário removido dos sudos", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Voltar", callback_data="settings_sudos")]] + ), + ) + + +@Client.on_callback_query(filters.regex("^settings_sudo_themc$") & filter_sudoers) +async def settings_sudo_themc(c: Client, cq: CallbackQuery): + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text=theme, callback_data=f"settings_sudo_themc {theme}" + ) + for theme in cards.keys() + ], + [ + InlineKeyboardButton( + "Adicionar novo", callback_data="settings_sudo_themc_new" + ), + InlineKeyboardButton("Voltar", callback_data="sudos"), + ], + ] + ) + await cq.edit_message_text("Selecione um tema:", reply_markup=keyb) + + +@Client.on_callback_query(filters.regex("^settings_sudo_themc_new$") & filter_sudoers) +async def settings_themes_new(c: Client, cq: CallbackQuery): + await cq.edit_message_text("Envie o nome do tema") + cmessage = None + # Wait for the user to send a message with the new emoji + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.text) + print(cmessage) + except ListenerTimeout: + return + + name = cmessage.text + + ncards = cards["classic"] + + for card in ncards["STICKERS"]: + await c.send_message( + cq.message.chat.id, "Mande um sticker para substituir o sticker:" + ) + await c.send_sticker(cq.message.chat.id, ncards["STICKERS"][card]) + + cmessage = None + # Wait for the user to send a message with the new emoji + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.sticker) + ncards["STICKERS"][card] = cmessage.sticker.file_id + except ListenerTimeout: + return + for card in ncards["STICKERS_GREY"]: + await c.send_message( + cq.message.chat.id, "Mande um sticker para substituir o sticker:" + ) + await c.send_sticker(cq.message.chat.id, ncards["STICKERS_GREY"][card]) + + cmessage = None + # Wait for the user to send a message with the new emoji + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.sticker) + ncards["STICKERS_GREY"][card] = cmessage.sticker.file_id + except ListenerTimeout: + return + + with open(f"cards/{name}.json", "w") as f: + dump(ncards, f) + + await c.send_message( + cq.message.chat.id, + "Tema adicionado com sucesso!", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Voltar", callback_data="settings_sudo_themc")]] + ), + ) + + +@Client.on_callback_query(filters.regex("^settings_sudo_themc ") & filter_sudoers) +async def settings_themes(c: Client, cq: CallbackQuery): + theme = cq.data.split(" ")[1] + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Adicionar", callback_data="settings_sudo_themc_add " + theme + ), + InlineKeyboardButton( + "Atualizar", callback_data="settings_sudo_themc_update " + theme + ), + InlineKeyboardButton( + "Verificar", callback_data="settings_sudo_themc_check " + theme + ), + ], + [InlineKeyboardButton("Voltar", callback_data="settings_sudo_themc")], + ] + ) + await cq.edit_message_text( + f"O que deseja fazer com o tema {theme}?", reply_markup=keyb + ) + + +@Client.on_callback_query(filters.regex("^settings_sudo_themc_add ") & filter_sudoers) +async def settings_themes_add(c: Client, cq: CallbackQuery): + theme = cq.data.split(" ")[1] + await cq.edit_message_text("Envie o código da nova carta") + + cmessage = None + + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.text) + except ListenerTimeout: + return + + code = cmessage.text + + await c.send_message(cq.message.chat.id, f"Mande um sticker colorido para o {code}") + + cmessage = None + + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.sticker) + except ListenerTimeout: + return + + stickerc = cmessage.sticker.file_id + + await c.send_message(cq.message.chat.id, f"Mande um sticker cinza para o {code}") + + cmessage = None + + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.sticker) + except ListenerTimeout: + return + + stickerg = cmessage.sticker.file_id + + cards[theme]["STICKERS"][code] = stickerc + cards[theme]["STICKERS_GREY"][code] = stickerg + + with open(f"cards/{theme}.json", "w") as f: + dump(cards[theme], f) + + await c.send_message( + cq.message.chat.id, + "Carta adicionada com sucesso!", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Voltar", callback_data="settings_sudo_themc")]] + ), + ) + + +@Client.on_callback_query( + filters.regex("^settings_sudo_themc_update ") & filter_sudoers +) +async def settings_themes_update(c: Client, cq: CallbackQuery): + sp = cq.data.split(" ")[1:] + print(sp) + theme = sp[0] + if len(sp) == 1: + keyb = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Light", + callback_data=f"settings_sudo_themc_update {theme} Light", + ), + InlineKeyboardButton( + "Dark", callback_data=f"settings_sudo_themc_update {theme} Dark" + ), + ], + ] + ) + await cq.edit_message_text( + "Escolha o tema que deseja atualizar", reply_markup=keyb + ) + if len(sp) == 2: + keyb = [] + for color in cards[theme]["CARDS"]["COLORS"]: + keyb.append( + InlineKeyboardButton( + color, + callback_data=f"settings_sudo_themc_update {theme} {sp[1]} {color}", + ) + ) + keyb.append( + InlineKeyboardButton( + "SPECIALS", + callback_data=f"settings_sudo_themc_update {theme} {sp[1]} SPECIALS", + ) + ) + await cq.edit_message_text( + "Escolha a cor da carta que deseja atualizar", + reply_markup=InlineKeyboardMarkup([keyb]), + ) + elif len(sp) == 3: + keyb = [] + if sp[2] == "SPECIALS": + for card in cards[theme]["CARDS"]["SPECIALS"]: + keyb.append( + [ + InlineKeyboardButton( + card, + callback_data=f"settings_sudo_themc_update {theme} {sp[1]} x {card}", + ) + ] + ) + else: + for card in cards[theme]["CARDS"]["VALUES"]: + keyb.append( + [ + InlineKeyboardButton( + card, + callback_data=f"settings_sudo_themc_update {theme} {sp[1]} {sp[2]} {card}", + ) + ] + ) + await cq.edit_message_text( + "Escolha o valor da carta que deseja atualizar", + reply_markup=InlineKeyboardMarkup(keyb), + ) + elif len(sp) == 4: + await cq.edit_message_text("Envie um sticker para substituir a carta:") + cardid = sp[2] + "_" + sp[3] if sp[2] != "x" else sp[3] + cardtm = "STICKERS_GREY" if sp[1] == "Dark" else "STICKERS" + await c.send_sticker(cq.message.chat.id, cards[theme][cardtm][cardid]) + + cmessage = None + + while cmessage is None: + try: + cmessage = await cq.message.chat.listen(filters.sticker) + except ListenerTimeout: + return + + cards[theme][cardtm][cardid] = cmessage.sticker.file_id + + with open(f"cards/{theme}.json", "w") as f: + dump(cards[theme], f) + + await c.send_message( + cq.message.chat.id, + "Carta atualizada com sucesso!", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Voltar", callback_data="settings_sudo_themc")]] + ), + ) + + +@Client.on_callback_query(filters.regex("^settings_sudo_themc_check ") & filter_sudoers) +async def settings_themes_check(c: Client, cq: CallbackQuery): + theme = cq.data.split(" ")[1] + card_types = ["STICKERS", "STICKERS_GREY"] + messages = ["Verificando cartas coloridas...", "Verificando cartas cinzas..."] + + for card_type, message in zip(card_types, messages): + await c.send_message(cq.message.chat.id, message) + for card in cards[theme][card_type]: + try: + m2 = await c.send_sticker( + cq.message.chat.id, cards[theme][card_type][card] + ) + await m2.delete() + await sleep(0.3) + except MediaEmpty: + await c.send_message( + cq.message.chat.id, + f"Carta {card} não encontrada, mande a carta para substitui-lá", + ) + + while True: + try: + cmessage = await cq.message.chat.listen(filters.sticker) + break + except ListenerTimeout: + return + + cards[theme][card_type][card] = cmessage.sticker.file_id + await c.send_message( + cq.message.chat.id, f"Carta {card} atualizada com sucesso!" + ) + + with open(f"cards/{theme}.json", "w") as f: + dump(cards[theme], f) + + await c.send_message(cq.message.chat.id, "Cartas verificadas com sucesso!") + + +@Client.on_inline_query(filters.regex("^change ") & filter_sudoers) +async def change(c: Client, iq: InlineQuery): + theme = iq.query.split(" ")[1] + articles = [] + for card in cards[theme]["STICKERS"]: + articles.append( + InlineQueryResultCachedSticker( + id="sudo" + card, sticker_file_id=cards[theme]["STICKERS"][card] + ) + ) + for card in cards[theme]["STICKERS_GREY"]: + articles.append( + InlineQueryResultCachedSticker( + id="sudog" + card, sticker_file_id=cards[theme]["STICKERS_GREY"][card] + ) + ) + return await iq.answer(articles, cache_time=0) diff --git a/spetials/minimalist.py b/spetials/minimalist.py new file mode 100644 index 0000000..a06af66 --- /dev/null +++ b/spetials/minimalist.py @@ -0,0 +1,32 @@ +from hydrogram import Client +from hydrogram.types import ChosenInlineResult +from hydrogram.types import InlineKeyboardMarkup, InlineKeyboardButton +from game import Game + +inline_keyb = InlineKeyboardMarkup( + [[InlineKeyboardButton("Jogar", switch_inline_query_current_chat="")]] +) + + +async def mirror(c: Client, ir: ChosenInlineResult, game: Game): + if game.draw < 2: + await c.send_message(game.chat.id, "Você não pode descartar essa carta agora!") + return True + else: + print(game.last_card_2, game.last_card) + game.players[ir.from_user.id].total_cards += 1 + ncard = ir.result_id.split("-")[2] + player = game.last_card_2["player"] + player = game.players[int(player)] + player.cards.extend(game.deck.draw(game.draw)) + await c.send_message( + game.chat.id, f"{player.mention} comprou {game.draw} cartas!" + ) + game.players[ir.from_user.id].cards.pop(int(ncard)) + await c.send_message( + game.chat.id, + f"Próximo: {game.next_player.mention}", + reply_markup=inline_keyb, + ) + game.draw = 0 + return True diff --git a/version.py b/version.py new file mode 100644 index 0000000..9f715b1 --- /dev/null +++ b/version.py @@ -0,0 +1,13 @@ +version = "1.0.0-beta" + +ascii_art = f""" ___ ___ ___ + /\\__\\ /\\__\\ /\\__\\ + /:/ _/_ /:| _|_ /:/ _/_ + /:/_/\\__\\ /::|/\\__\\ /:/_/\\__\\ + \\:\\/:/ / \\/|::/ / \\:\\/:/ / + \\::/ / |:/ / \\::/ / _ _ |_ _ |_ + \\/__/ \\/__/ \\/__/ | (_)|_)(_)|_ + + Version: {version} - Powered by Hydrogram + +"""