Compare commits
27 commits
Author | SHA1 | Date | |
---|---|---|---|
b9fd7efbcb | |||
fc51c39101 | |||
|
7af19fae31 | ||
|
09086f986a | ||
|
bda53c1b71 | ||
|
f482af3330 | ||
|
79c16e3920 | ||
|
1e532ca6fc | ||
|
e25879cead | ||
|
0d936c961d | ||
|
82471f5120 | ||
|
68c04a9bd9 | ||
|
034dd5ab34 | ||
|
43e0479c6f | ||
|
6a467a9d0c | ||
|
1785da37b5 | ||
|
56f623e321 | ||
|
f16bf317b5 | ||
|
961a9ced76 | ||
|
ece86473a2 | ||
|
cb4d9bd5e3 | ||
|
c9e52174e1 | ||
|
f11df72b0b | ||
|
1b7aea4320 | ||
|
4021c1483e | ||
|
a8d5132dc5 | ||
|
6109c90d06 |
60
.github/workflows/docker-publish.yml
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
name: Docker build
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '25 15 * * *'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Use docker.io for Docker Hub if empty
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
# This is used to complete the identity challenge
|
||||||
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||||
|
- name: Setup Docker buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v4
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ github.repository }}
|
||||||
|
tags: |
|
||||||
|
type=schedule
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: build-and-push
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
6
.gitignore
vendored
|
@ -27,6 +27,8 @@ var/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
|
|
||||||
|
venv/
|
||||||
|
|
||||||
# PyInstaller
|
# PyInstaller
|
||||||
# Usually these files are written by a python script from a template
|
# Usually these files are written by a python script from a template
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
@ -69,3 +71,7 @@ target/
|
||||||
|
|
||||||
# Database file
|
# Database file
|
||||||
uno.sqlite3
|
uno.sqlite3
|
||||||
|
|
||||||
|
images/api_auth.json
|
||||||
|
images/sticker_config.json
|
||||||
|
images/sticker_uploader.session
|
||||||
|
|
|
@ -10,7 +10,6 @@ COPY . .
|
||||||
|
|
||||||
RUN cd locales && find . -maxdepth 2 -type d -name 'LC_MESSAGES' -exec ash -c 'msgfmt {}/unobot.po -o {}/unobot.mo' \;
|
RUN cd locales && find . -maxdepth 2 -type d -name 'LC_MESSAGES' -exec ash -c 'msgfmt {}/unobot.po -o {}/unobot.mo' \;
|
||||||
|
|
||||||
RUN pip install -r requirements.txt
|
|
||||||
|
|
||||||
VOLUME /app/data
|
VOLUME /app/data
|
||||||
ENV UNO_DB /app/data/uno.sqlite3
|
ENV UNO_DB /app/data/uno.sqlite3
|
||||||
|
|
5
Pipfile
|
@ -4,10 +4,11 @@ url = "https://pypi.org/simple"
|
||||||
verify_ssl = true
|
verify_ssl = true
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
telethon = "*"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
python-telegram-bot = "==8.1.1"
|
python-telegram-bot = "==13.11"
|
||||||
pony = "*"
|
pony = "*"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.7"
|
python_version = "3.11"
|
||||||
|
|
150
Pipfile.lock
generated
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "de56c4d5f516205e99d141cd7d372f67b602b6f981306971c01ffe25a5abf5c6"
|
"sha256": "87f82f4abefdefd3b212fa99f5cbf6e222d6855aa7574d7a94fbf51b33cc342f"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
"python_version": "3.7"
|
"python_version": "3.11"
|
||||||
},
|
},
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
|
@ -16,34 +16,150 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
|
"apscheduler": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3bb5229eed6fbbdafc13ce962712ae66e175aa214c69bed35a06bffcf0c5e244",
|
||||||
|
"sha256:e8b1ecdb4c7cb2818913f766d5898183c7cb8936680710a4d3a966e02262e526"
|
||||||
|
],
|
||||||
|
"version": "==3.6.3"
|
||||||
|
},
|
||||||
|
"cachetools": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
|
||||||
|
"sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
|
||||||
|
],
|
||||||
|
"markers": "python_version ~= '3.5'",
|
||||||
|
"version": "==4.2.2"
|
||||||
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
|
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||||
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
|
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||||
],
|
],
|
||||||
"version": "==2019.6.16"
|
"markers": "python_version >= '3.6'",
|
||||||
},
|
"version": "==2022.12.7"
|
||||||
"future": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
|
|
||||||
],
|
|
||||||
"version": "==0.17.1"
|
|
||||||
},
|
},
|
||||||
"pony": {
|
"pony": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:55bb9d4d12029d8c2bbbc7a284970e72225035db7e6370c0a15ec93d1886fe88"
|
"sha256:5f45fc67587f4520c560a57148cc573b097d42f82f5cb200d72c957b5708198d",
|
||||||
|
"sha256:608a1c1d662983bad2590e650f2bbc1cd6ed48558894ad8f50da4739ff98f614"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.7.10"
|
"version": "==0.7.16"
|
||||||
},
|
},
|
||||||
"python-telegram-bot": {
|
"python-telegram-bot": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:238c4a88b09d93c52d413bcf7e7fe14dfeb02f5f9222ffe4cafd4bd3d55489a3",
|
"sha256:534f5bb0ff4ca34c9252e97e0b3bcdab81d97be0eb4821682a361cb426c00e55",
|
||||||
"sha256:997983e5082dc6aa811bce3a6014731201fc64b0a9c02fdb26beac686029d94b"
|
"sha256:baeff704baa2ac3dc17a944c02da888228ad258e89be2e5bcbd13a8a5102d573"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==8.1.1"
|
"version": "==13.11"
|
||||||
|
},
|
||||||
|
"pytz": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588",
|
||||||
|
"sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"
|
||||||
|
],
|
||||||
|
"version": "==2023.3"
|
||||||
|
},
|
||||||
|
"pytz-deprecation-shim": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6",
|
||||||
|
"sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||||
|
"version": "==0.1.0.post0"
|
||||||
|
},
|
||||||
|
"setuptools": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:257de92a9d50a60b8e22abfcbb771571fde0dbf3ec234463212027a4eeecbe9a",
|
||||||
|
"sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==67.6.1"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||||
|
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==1.16.0"
|
||||||
|
},
|
||||||
|
"tornado": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca",
|
||||||
|
"sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72",
|
||||||
|
"sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23",
|
||||||
|
"sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8",
|
||||||
|
"sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b",
|
||||||
|
"sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9",
|
||||||
|
"sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13",
|
||||||
|
"sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75",
|
||||||
|
"sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac",
|
||||||
|
"sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e",
|
||||||
|
"sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==6.2"
|
||||||
|
},
|
||||||
|
"tzdata": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a",
|
||||||
|
"sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"
|
||||||
|
],
|
||||||
|
"markers": "platform_system == 'Windows'",
|
||||||
|
"version": "==2023.3"
|
||||||
|
},
|
||||||
|
"tzlocal": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3f21d09e1b2aa9f2dacca12da240ca37de3ba5237a93addfd6d593afe9073355",
|
||||||
|
"sha256:b44c4388f3d34f25862cfbb387578a4d70fec417649da694a132f628a23367e2"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {}
|
"develop": {
|
||||||
|
"pyaes": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f"
|
||||||
|
],
|
||||||
|
"version": "==1.6.1"
|
||||||
|
},
|
||||||
|
"pyasn1": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
|
||||||
|
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
|
||||||
|
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
|
||||||
|
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
|
||||||
|
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
|
||||||
|
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
|
||||||
|
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
|
||||||
|
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
|
||||||
|
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
|
||||||
|
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
|
||||||
|
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
|
||||||
|
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
|
||||||
|
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
|
||||||
|
],
|
||||||
|
"version": "==0.4.8"
|
||||||
|
},
|
||||||
|
"rsa": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7",
|
||||||
|
"sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||||
|
"version": "==4.9"
|
||||||
|
},
|
||||||
|
"telethon": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:613bae42acb5f2eeb1a0b92614e323021c66f374db62adf9826ea0c2c9120bb1",
|
||||||
|
"sha256:893c10f133974fba4c53eb1736b6514d596d1cd94c83436a711f3345df945199"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.28.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,18 @@
|
||||||
|
|
||||||
The following awesome people contributed to this project by translating it:
|
The following awesome people contributed to this project by translating it:
|
||||||
|
|
||||||
| Locale | Translators |
|
| Locale | Translators |
|
||||||
|--------|-------------|
|
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| ca_CA | [retiolus](https://github.com/retiolus) |
|
| ca_CA | [retiolus](https://github.com/retiolus) |
|
||||||
| de_DE | [Jannes Höke](https://github.com/jh0ker) |
|
| de_DE | [Jannes Höke](https://github.com/jh0ker) |
|
||||||
| es_ES | [José.A Rojo](https://github.com/J4RV), [Ricardo Valverde Hernández](https://telegram.me/rivh1), Victor, Yuga |
|
| es_ES | [José.A Rojo](https://github.com/J4RV), [Ricardo Valverde Hernández](https://telegram.me/rivh1), Victor, Yuga |
|
||||||
| id_ID | [Erwin Guo](https://www.facebook.com/erwinfransiscus) |
|
| id_ID | [Erwin Guo](https://www.facebook.com/erwinfransiscus) |
|
||||||
| it_IT | Carola Mariano, ɳick |
|
| it_IT | Carola Mariano, ɳick |
|
||||||
| ml_IN | [Adhith T](https://github.com/adhitht123) |
|
| ml_IN | [Adhith T](https://github.com/adhitht123) |
|
||||||
| pt_BR | [Iuri Guilherme](https://github.com/iuriguilherme), [João Rodrigo Couto de Oliveira](http://twitter.com/JoaoRodrigoJR) |
|
| pt_BR | [Iuri Guilherme](https://github.com/iuriguilherme), [João Rodrigo Couto de Oliveira](http://twitter.com/JoaoRodrigoJR) |
|
||||||
| zh_CN | [imlonghao](https://github.com/imlonghao), [XhyEax](https://github.com/XhyEax) |
|
| vi_VN | [Lê Minh Sơn](https://github.com/leminhson06) |
|
||||||
| zh_HK | [Jed Cheng](https://www.facebook.com/profile.php?id=100002258388821) |
|
| zh_CN | [imlonghao](https://github.com/imlonghao), [XhyEax](https://github.com/XhyEax) |
|
||||||
|
| zh_HK | [Jed Cheng](https://www.facebook.com/profile.php?id=100002258388821) |
|
||||||
| zh_TW | [Eugene Lam](https://www.facebook.com/eugenelam1118), [jimchen5209](https://www.youtube.com/user/jimchen5209), [pan93412](https://www.github.com/pan93412) |
|
| zh_TW | [Eugene Lam](https://www.facebook.com/eugenelam1118), [jimchen5209](https://www.youtube.com/user/jimchen5209), [pan93412](https://www.github.com/pan93412) |
|
||||||
|
|
||||||
Please add yourself here alphabetically when you submit your first translation.
|
Please add yourself here alphabetically when you submit your first translation.
|
||||||
|
|
36
actions.py
|
@ -6,6 +6,8 @@ import card as c
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from telegram import Message, Chat
|
from telegram import Message, Chat
|
||||||
|
from telegram.ext import CallbackContext
|
||||||
|
from apscheduler.jobstores.base import JobLookupError
|
||||||
|
|
||||||
from config import TIME_REMOVAL_AFTER_SKIP, MIN_FAST_TURN_TIME
|
from config import TIME_REMOVAL_AFTER_SKIP, MIN_FAST_TURN_TIME
|
||||||
from errors import DeckEmptyError, NotEnoughPlayersError
|
from errors import DeckEmptyError, NotEnoughPlayersError
|
||||||
|
@ -111,7 +113,7 @@ def do_play_card(bot, player, result_id):
|
||||||
if us.stats:
|
if us.stats:
|
||||||
us.games_played += 1
|
us.games_played += 1
|
||||||
|
|
||||||
if game.players_won is 0:
|
if game.players_won == 0:
|
||||||
us.first_places += 1
|
us.first_places += 1
|
||||||
|
|
||||||
game.players_won += 1
|
game.players_won += 1
|
||||||
|
@ -153,11 +155,15 @@ def do_call_bluff(bot, player):
|
||||||
chat = game.chat
|
chat = game.chat
|
||||||
|
|
||||||
if player.prev.bluffing:
|
if player.prev.bluffing:
|
||||||
|
draw_prev = 4
|
||||||
|
draw_next = game.draw_counter - draw_prev
|
||||||
|
draw_next_text = ". " + __("Giving {count} cards to {name}").format(count=draw_next, name=player.user.first_name) if draw_next > 0 else ""
|
||||||
send_async(bot, chat.id,
|
send_async(bot, chat.id,
|
||||||
text=__("Bluff called! Giving 4 cards to {name}",
|
text=__("Bluff called! Giving {count} cards to {name}" + draw_next_text,
|
||||||
multi=game.translate)
|
multi=game.translate)
|
||||||
.format(name=player.prev.user.first_name))
|
.format(name=player.prev.user.first_name, count=draw_prev))
|
||||||
|
|
||||||
|
game.draw_counter = draw_prev
|
||||||
try:
|
try:
|
||||||
player.prev.draw()
|
player.prev.draw()
|
||||||
except DeckEmptyError:
|
except DeckEmptyError:
|
||||||
|
@ -165,12 +171,21 @@ def do_call_bluff(bot, player):
|
||||||
text=__("There are no more cards in the deck.",
|
text=__("There are no more cards in the deck.",
|
||||||
multi=game.translate))
|
multi=game.translate))
|
||||||
|
|
||||||
|
game.draw_counter = draw_next
|
||||||
|
try:
|
||||||
|
player.draw()
|
||||||
|
except DeckEmptyError:
|
||||||
|
send_async(bot, player.game.chat.id,
|
||||||
|
text=__("There are no more cards in the deck.",
|
||||||
|
multi=game.translate))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
game.draw_counter += 2
|
game.draw_counter += 2
|
||||||
send_async(bot, chat.id,
|
send_async(bot, chat.id,
|
||||||
text=__("{name1} didn't bluff! Giving 6 cards to {name2}",
|
text=__("{name1} didn't bluff! Giving {count} cards to {name2}",
|
||||||
multi=game.translate)
|
multi=game.translate)
|
||||||
.format(name1=player.prev.user.first_name,
|
.format(name1=player.prev.user.first_name,
|
||||||
|
count=game.draw_counter,
|
||||||
name2=player.user.first_name))
|
name2=player.user.first_name))
|
||||||
try:
|
try:
|
||||||
player.draw()
|
player.draw()
|
||||||
|
@ -191,7 +206,10 @@ def start_player_countdown(bot, game, job_queue):
|
||||||
|
|
||||||
if game.mode == 'fast':
|
if game.mode == 'fast':
|
||||||
if game.job:
|
if game.job:
|
||||||
game.job.schedule_removal()
|
try:
|
||||||
|
game.job.schedule_removal()
|
||||||
|
except JobLookupError:
|
||||||
|
pass
|
||||||
|
|
||||||
job = job_queue.run_once(
|
job = job_queue.run_once(
|
||||||
#lambda x,y: do_skip(bot, player),
|
#lambda x,y: do_skip(bot, player),
|
||||||
|
@ -205,9 +223,9 @@ def start_player_countdown(bot, game, job_queue):
|
||||||
player.game.job = job
|
player.game.job = job
|
||||||
|
|
||||||
|
|
||||||
def skip_job(bot, job):
|
def skip_job(context: CallbackContext):
|
||||||
player = job.context.player
|
player = context.job.context.player
|
||||||
game = player.game
|
game = player.game
|
||||||
if game_is_running(game):
|
if game_is_running(game):
|
||||||
job_queue = job.context.job_queue
|
job_queue = context.job.context.job_queue
|
||||||
do_skip(bot, player, job_queue)
|
do_skip(context.bot, player, job_queue)
|
||||||
|
|
203
bot.py
|
@ -21,9 +21,9 @@ import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from telegram import ParseMode, InlineKeyboardMarkup, \
|
from telegram import ParseMode, InlineKeyboardMarkup, \
|
||||||
InlineKeyboardButton
|
InlineKeyboardButton, Update
|
||||||
from telegram.ext import InlineQueryHandler, ChosenInlineResultHandler, \
|
from telegram.ext import InlineQueryHandler, ChosenInlineResultHandler, \
|
||||||
CommandHandler, MessageHandler, Filters, CallbackQueryHandler
|
CommandHandler, MessageHandler, Filters, CallbackQueryHandler, CallbackContext
|
||||||
from telegram.ext.dispatcher import run_async
|
from telegram.ext.dispatcher import run_async
|
||||||
|
|
||||||
import card as c
|
import card as c
|
||||||
|
@ -49,9 +49,10 @@ logging.basicConfig(
|
||||||
level=logging.INFO
|
level=logging.INFO
|
||||||
)
|
)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
logging.getLogger('apscheduler').setLevel(logging.WARNING)
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def notify_me(bot, update):
|
def notify_me(update: Update, context: CallbackContext):
|
||||||
"""Handler for /notify_me command, pm people for next game"""
|
"""Handler for /notify_me command, pm people for next game"""
|
||||||
chat_id = update.message.chat_id
|
chat_id = update.message.chat_id
|
||||||
if update.message.chat.type == 'private':
|
if update.message.chat.type == 'private':
|
||||||
|
@ -67,15 +68,25 @@ def notify_me(bot, update):
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def new_game(bot, update):
|
def new_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /new command"""
|
"""Handler for the /new command"""
|
||||||
chat_id = update.message.chat_id
|
chat_id = update.message.chat_id
|
||||||
|
|
||||||
if update.message.chat.type == 'private':
|
if update.message.chat.type == 'private':
|
||||||
help_handler(bot, update)
|
help_handler(update, context)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
try:
|
||||||
|
_game = gm.chatid_games[chat_id][-1]
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
send_async(bot, chat_id,
|
||||||
|
text=_("There is already a game running in this chat. Join the "
|
||||||
|
"game with /join"))
|
||||||
|
return
|
||||||
|
|
||||||
if update.message.chat_id in gm.remind_dict:
|
if update.message.chat_id in gm.remind_dict:
|
||||||
for user in gm.remind_dict[update.message.chat_id]:
|
for user in gm.remind_dict[update.message.chat_id]:
|
||||||
send_async(bot,
|
send_async(bot,
|
||||||
|
@ -89,88 +100,88 @@ def new_game(bot, update):
|
||||||
game.starter = update.message.from_user
|
game.starter = update.message.from_user
|
||||||
game.owner.append(update.message.from_user.id)
|
game.owner.append(update.message.from_user.id)
|
||||||
game.mode = DEFAULT_GAMEMODE
|
game.mode = DEFAULT_GAMEMODE
|
||||||
send_async(bot, chat_id,
|
send_async(context.bot, chat_id,
|
||||||
text=_("Created a new game! Join the game with /join "
|
text=_("Created a new game! Join the game with /join "
|
||||||
"and start the game with /start"))
|
"and start the game with /start"))
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def kill_game(bot, update):
|
def kill_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /kill command"""
|
"""Handler for the /kill command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
games = gm.chatid_games.get(chat.id)
|
games = gm.chatid_games.get(chat.id)
|
||||||
|
|
||||||
if update.message.chat.type == 'private':
|
if update.message.chat.type == 'private':
|
||||||
help_handler(bot, update)
|
help_handler(update, context)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not games:
|
if not games:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("There is no running game in this chat."))
|
text=_("There is no running game in this chat."))
|
||||||
return
|
return
|
||||||
|
|
||||||
game = games[-1]
|
game = games[-1]
|
||||||
|
|
||||||
if user_is_creator_or_admin(user, game, bot, chat):
|
if user_is_creator_or_admin(user, game, context.bot, chat):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gm.end_game(chat, user)
|
gm.end_game(chat, user)
|
||||||
send_async(bot, chat.id, text=__("Game ended!", multi=game.translate))
|
send_async(context.bot, chat.id, text=__("Game ended!", multi=game.translate))
|
||||||
|
|
||||||
except NoGameInChatError:
|
except NoGameInChatError:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("The game is not started yet. "
|
text=_("The game is not started yet. "
|
||||||
"Join the game with /join and start the game with /start"),
|
"Join the game with /join and start the game with /start"),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Only the game creator ({name}) and admin can do that.")
|
text=_("Only the game creator ({name}) and admin can do that.")
|
||||||
.format(name=game.starter.first_name),
|
.format(name=game.starter.first_name),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def join_game(bot, update):
|
def join_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /join command"""
|
"""Handler for the /join command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
|
|
||||||
if update.message.chat.type == 'private':
|
if update.message.chat.type == 'private':
|
||||||
help_handler(bot, update)
|
help_handler(update, context)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gm.join_game(update.message.from_user, chat)
|
gm.join_game(update.message.from_user, chat)
|
||||||
|
|
||||||
except LobbyClosedError:
|
except LobbyClosedError:
|
||||||
send_async(bot, chat.id, text=_("The lobby is closed"))
|
send_async(context.bot, chat.id, text=_("The lobby is closed"))
|
||||||
|
|
||||||
except NoGameInChatError:
|
except NoGameInChatError:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("No game is running at the moment. "
|
text=_("No game is running at the moment. "
|
||||||
"Create a new game with /new"),
|
"Create a new game with /new"),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
except AlreadyJoinedError:
|
except AlreadyJoinedError:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("You already joined the game. Start the game "
|
text=_("You already joined the game. Start the game "
|
||||||
"with /start"),
|
"with /start"),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
except DeckEmptyError:
|
except DeckEmptyError:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("There are not enough cards left in the deck for "
|
text=_("There are not enough cards left in the deck for "
|
||||||
"new players to join."),
|
"new players to join."),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Joined the game"),
|
text=_("Joined the game"),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def leave_game(bot, update):
|
def leave_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /leave command"""
|
"""Handler for the /leave command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
|
@ -178,7 +189,7 @@ def leave_game(bot, update):
|
||||||
player = gm.player_for_user_in_chat(user, chat)
|
player = gm.player_for_user_in_chat(user, chat)
|
||||||
|
|
||||||
if player is None:
|
if player is None:
|
||||||
send_async(bot, chat.id, text=_("You are not playing in a game in "
|
send_async(context.bot, chat.id, text=_("You are not playing in a game in "
|
||||||
"this group."),
|
"this group."),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
return
|
return
|
||||||
|
@ -190,23 +201,23 @@ def leave_game(bot, update):
|
||||||
gm.leave_game(user, chat)
|
gm.leave_game(user, chat)
|
||||||
|
|
||||||
except NoGameInChatError:
|
except NoGameInChatError:
|
||||||
send_async(bot, chat.id, text=_("You are not playing in a game in "
|
send_async(context.bot, chat.id, text=_("You are not playing in a game in "
|
||||||
"this group."),
|
"this group."),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
except NotEnoughPlayersError:
|
except NotEnoughPlayersError:
|
||||||
gm.end_game(chat, user)
|
gm.end_game(chat, user)
|
||||||
send_async(bot, chat.id, text=__("Game ended!", multi=game.translate))
|
send_async(context.bot, chat.id, text=__("Game ended!", multi=game.translate))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if game.started:
|
if game.started:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=__("Okay. Next Player: {name}",
|
text=__("Okay. Next Player: {name}",
|
||||||
multi=game.translate).format(
|
multi=game.translate).format(
|
||||||
name=display_name(game.current_player.user)),
|
name=display_name(game.current_player.user)),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=__("{name} left the game before it started.",
|
text=__("{name} left the game before it started.",
|
||||||
multi=game.translate).format(
|
multi=game.translate).format(
|
||||||
name=display_name(user)),
|
name=display_name(user)),
|
||||||
|
@ -214,11 +225,11 @@ def leave_game(bot, update):
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def kick_player(bot, update):
|
def kick_player(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /kick command"""
|
"""Handler for the /kick command"""
|
||||||
|
|
||||||
if update.message.chat.type == 'private':
|
if update.message.chat.type == 'private':
|
||||||
help_handler(bot, update)
|
help_handler(update, context)
|
||||||
return
|
return
|
||||||
|
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
|
@ -228,20 +239,20 @@ def kick_player(bot, update):
|
||||||
game = gm.chatid_games[chat.id][-1]
|
game = gm.chatid_games[chat.id][-1]
|
||||||
|
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("No game is running at the moment. "
|
text=_("No game is running at the moment. "
|
||||||
"Create a new game with /new"),
|
"Create a new game with /new"),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not game.started:
|
if not game.started:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("The game is not started yet. "
|
text=_("The game is not started yet. "
|
||||||
"Join the game with /join and start the game with /start"),
|
"Join the game with /join and start the game with /start"),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
if user_is_creator_or_admin(user, game, bot, chat):
|
if user_is_creator_or_admin(user, game, context.bot, chat):
|
||||||
|
|
||||||
if update.message.reply_to_message:
|
if update.message.reply_to_message:
|
||||||
kicked = update.message.reply_to_message.from_user
|
kicked = update.message.reply_to_message.from_user
|
||||||
|
@ -250,40 +261,40 @@ def kick_player(bot, update):
|
||||||
gm.leave_game(kicked, chat)
|
gm.leave_game(kicked, chat)
|
||||||
|
|
||||||
except NoGameInChatError:
|
except NoGameInChatError:
|
||||||
send_async(bot, chat.id, text=_("Player {name} is not found in the current game.".format(name=display_name(kicked))),
|
send_async(context.bot, chat.id, text=_("Player {name} is not found in the current game.".format(name=display_name(kicked))),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
except NotEnoughPlayersError:
|
except NotEnoughPlayersError:
|
||||||
gm.end_game(chat, user)
|
gm.end_game(chat, user)
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("{0} was kicked by {1}".format(display_name(kicked), display_name(user))))
|
text=_("{0} was kicked by {1}".format(display_name(kicked), display_name(user))))
|
||||||
send_async(bot, chat.id, text=__("Game ended!", multi=game.translate))
|
send_async(context.bot, chat.id, text=__("Game ended!", multi=game.translate))
|
||||||
return
|
return
|
||||||
|
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("{0} was kicked by {1}".format(display_name(kicked), display_name(user))))
|
text=_("{0} was kicked by {1}".format(display_name(kicked), display_name(user))))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Please reply to the person you want to kick and type /kick again."),
|
text=_("Please reply to the person you want to kick and type /kick again."),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=__("Okay. Next Player: {name}",
|
text=__("Okay. Next Player: {name}",
|
||||||
multi=game.translate).format(
|
multi=game.translate).format(
|
||||||
name=display_name(game.current_player.user)),
|
name=display_name(game.current_player.user)),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Only the game creator ({name}) and admin can do that.")
|
text=_("Only the game creator ({name}) and admin can do that.")
|
||||||
.format(name=game.starter.first_name),
|
.format(name=game.starter.first_name),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
|
|
||||||
def select_game(bot, update):
|
def select_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for callback queries to select the current game"""
|
"""Handler for callback queries to select the current game"""
|
||||||
|
|
||||||
chat_id = int(update.callback_query.data)
|
chat_id = int(update.callback_query.data)
|
||||||
|
@ -299,16 +310,15 @@ def select_game(bot, update):
|
||||||
text=_("Game not found."))
|
text=_("Game not found."))
|
||||||
return
|
return
|
||||||
|
|
||||||
@run_async
|
def selected():
|
||||||
def selected(bot):
|
|
||||||
back = [[InlineKeyboardButton(text=_("Back to last group"),
|
back = [[InlineKeyboardButton(text=_("Back to last group"),
|
||||||
switch_inline_query='')]]
|
switch_inline_query='')]]
|
||||||
bot.answerCallbackQuery(update.callback_query.id,
|
context.bot.answerCallbackQuery(update.callback_query.id,
|
||||||
text=_("Please switch to the group you selected!"),
|
text=_("Please switch to the group you selected!"),
|
||||||
show_alert=False,
|
show_alert=False,
|
||||||
timeout=TIMEOUT)
|
timeout=TIMEOUT)
|
||||||
|
|
||||||
bot.editMessageText(chat_id=update.callback_query.message.chat_id,
|
context.bot.editMessageText(chat_id=update.callback_query.message.chat_id,
|
||||||
message_id=update.callback_query.message.message_id,
|
message_id=update.callback_query.message.message_id,
|
||||||
text=_("Selected group: {group}\n"
|
text=_("Selected group: {group}\n"
|
||||||
"<b>Make sure that you switch to the correct "
|
"<b>Make sure that you switch to the correct "
|
||||||
|
@ -318,11 +328,11 @@ def select_game(bot, update):
|
||||||
parse_mode=ParseMode.HTML,
|
parse_mode=ParseMode.HTML,
|
||||||
timeout=TIMEOUT)
|
timeout=TIMEOUT)
|
||||||
|
|
||||||
selected(bot)
|
dispatcher.run_async(selected)
|
||||||
|
|
||||||
|
|
||||||
@game_locales
|
@game_locales
|
||||||
def status_update(bot, update):
|
def status_update(update: Update, context: CallbackContext):
|
||||||
"""Remove player from game if user leaves the group"""
|
"""Remove player from game if user leaves the group"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
|
|
||||||
|
@ -337,17 +347,17 @@ def status_update(bot, update):
|
||||||
pass
|
pass
|
||||||
except NotEnoughPlayersError:
|
except NotEnoughPlayersError:
|
||||||
gm.end_game(chat, user)
|
gm.end_game(chat, user)
|
||||||
send_async(bot, chat.id, text=__("Game ended!",
|
send_async(context.bot, chat.id, text=__("Game ended!",
|
||||||
multi=game.translate))
|
multi=game.translate))
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id, text=__("Removing {name} from the game",
|
send_async(context.bot, chat.id, text=__("Removing {name} from the game",
|
||||||
multi=game.translate)
|
multi=game.translate)
|
||||||
.format(name=display_name(user)))
|
.format(name=display_name(user)))
|
||||||
|
|
||||||
|
|
||||||
@game_locales
|
@game_locales
|
||||||
@user_locale
|
@user_locale
|
||||||
def start_game(bot, update, args, job_queue):
|
def start_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /start command"""
|
"""Handler for the /start command"""
|
||||||
|
|
||||||
if update.message.chat.type != 'private':
|
if update.message.chat.type != 'private':
|
||||||
|
@ -356,16 +366,16 @@ def start_game(bot, update, args, job_queue):
|
||||||
try:
|
try:
|
||||||
game = gm.chatid_games[chat.id][-1]
|
game = gm.chatid_games[chat.id][-1]
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("There is no game running in this chat. Create "
|
text=_("There is no game running in this chat. Create "
|
||||||
"a new one with /new"))
|
"a new one with /new"))
|
||||||
return
|
return
|
||||||
|
|
||||||
if game.started:
|
if game.started:
|
||||||
send_async(bot, chat.id, text=_("The game has already started"))
|
send_async(context.bot, chat.id, text=_("The game has already started"))
|
||||||
|
|
||||||
elif len(game.players) < MIN_PLAYERS:
|
elif len(game.players) < MIN_PLAYERS:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=__("At least {minplayers} players must /join the game "
|
text=__("At least {minplayers} players must /join the game "
|
||||||
"before you can start it").format(minplayers=MIN_PLAYERS))
|
"before you can start it").format(minplayers=MIN_PLAYERS))
|
||||||
|
|
||||||
|
@ -383,30 +393,29 @@ def start_game(bot, update, args, job_queue):
|
||||||
multi=game.translate)
|
multi=game.translate)
|
||||||
.format(name=display_name(game.current_player.user)))
|
.format(name=display_name(game.current_player.user)))
|
||||||
|
|
||||||
@run_async
|
|
||||||
def send_first():
|
def send_first():
|
||||||
"""Send the first card and player"""
|
"""Send the first card and player"""
|
||||||
|
|
||||||
bot.sendSticker(chat.id,
|
context.bot.sendSticker(chat.id,
|
||||||
sticker=c.STICKERS[str(game.last_card)],
|
sticker=c.STICKERS[str(game.last_card)],
|
||||||
timeout=TIMEOUT)
|
timeout=TIMEOUT)
|
||||||
|
|
||||||
bot.sendMessage(chat.id,
|
context.bot.sendMessage(chat.id,
|
||||||
text=first_message,
|
text=first_message,
|
||||||
reply_markup=InlineKeyboardMarkup(choice),
|
reply_markup=InlineKeyboardMarkup(choice),
|
||||||
timeout=TIMEOUT)
|
timeout=TIMEOUT)
|
||||||
|
|
||||||
send_first()
|
dispatcher.run_async(send_first)
|
||||||
start_player_countdown(bot, game, job_queue)
|
start_player_countdown(context.bot, game, context.job_queue)
|
||||||
|
|
||||||
elif len(args) and args[0] == 'select':
|
elif len(context.args) and context.args[0] == 'select':
|
||||||
players = gm.userid_players[update.message.from_user.id]
|
players = gm.userid_players[update.message.from_user.id]
|
||||||
|
|
||||||
groups = list()
|
groups = list()
|
||||||
for player in players:
|
for player in players:
|
||||||
title = player.game.chat.title
|
title = player.game.chat.title
|
||||||
|
|
||||||
if player is gm.userid_current[update.message.from_user.id]:
|
if player == gm.userid_current[update.message.from_user.id]:
|
||||||
title = '- %s -' % player.game.chat.title
|
title = '- %s -' % player.game.chat.title
|
||||||
|
|
||||||
groups.append(
|
groups.append(
|
||||||
|
@ -414,23 +423,23 @@ def start_game(bot, update, args, job_queue):
|
||||||
callback_data=str(player.game.chat.id))]
|
callback_data=str(player.game.chat.id))]
|
||||||
)
|
)
|
||||||
|
|
||||||
send_async(bot, update.message.chat_id,
|
send_async(context.bot, update.message.chat_id,
|
||||||
text=_('Please select the group you want to play in.'),
|
text=_('Please select the group you want to play in.'),
|
||||||
reply_markup=InlineKeyboardMarkup(groups))
|
reply_markup=InlineKeyboardMarkup(groups))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
help_handler(bot, update)
|
help_handler(update, context)
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def close_game(bot, update):
|
def close_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /close command"""
|
"""Handler for the /close command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
games = gm.chatid_games.get(chat.id)
|
games = gm.chatid_games.get(chat.id)
|
||||||
|
|
||||||
if not games:
|
if not games:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("There is no running game in this chat."))
|
text=_("There is no running game in this chat."))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -438,12 +447,12 @@ def close_game(bot, update):
|
||||||
|
|
||||||
if user.id in game.owner:
|
if user.id in game.owner:
|
||||||
game.open = False
|
game.open = False
|
||||||
send_async(bot, chat.id, text=_("Closed the lobby. "
|
send_async(context.bot, chat.id, text=_("Closed the lobby. "
|
||||||
"No more players can join this game."))
|
"No more players can join this game."))
|
||||||
return
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Only the game creator ({name}) and admin can do that.")
|
text=_("Only the game creator ({name}) and admin can do that.")
|
||||||
.format(name=game.starter.first_name),
|
.format(name=game.starter.first_name),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
@ -451,14 +460,14 @@ def close_game(bot, update):
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def open_game(bot, update):
|
def open_game(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /open command"""
|
"""Handler for the /open command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
games = gm.chatid_games.get(chat.id)
|
games = gm.chatid_games.get(chat.id)
|
||||||
|
|
||||||
if not games:
|
if not games:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("There is no running game in this chat."))
|
text=_("There is no running game in this chat."))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -466,11 +475,11 @@ def open_game(bot, update):
|
||||||
|
|
||||||
if user.id in game.owner:
|
if user.id in game.owner:
|
||||||
game.open = True
|
game.open = True
|
||||||
send_async(bot, chat.id, text=_("Opened the lobby. "
|
send_async(context.bot, chat.id, text=_("Opened the lobby. "
|
||||||
"New players may /join the game."))
|
"New players may /join the game."))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Only the game creator ({name}) and admin can do that.")
|
text=_("Only the game creator ({name}) and admin can do that.")
|
||||||
.format(name=game.starter.first_name),
|
.format(name=game.starter.first_name),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
@ -478,14 +487,14 @@ def open_game(bot, update):
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def enable_translations(bot, update):
|
def enable_translations(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /enable_translations command"""
|
"""Handler for the /enable_translations command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
games = gm.chatid_games.get(chat.id)
|
games = gm.chatid_games.get(chat.id)
|
||||||
|
|
||||||
if not games:
|
if not games:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("There is no running game in this chat."))
|
text=_("There is no running game in this chat."))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -493,12 +502,12 @@ def enable_translations(bot, update):
|
||||||
|
|
||||||
if user.id in game.owner:
|
if user.id in game.owner:
|
||||||
game.translate = True
|
game.translate = True
|
||||||
send_async(bot, chat.id, text=_("Enabled multi-translations. "
|
send_async(context.bot, chat.id, text=_("Enabled multi-translations. "
|
||||||
"Disable with /disable_translations"))
|
"Disable with /disable_translations"))
|
||||||
return
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Only the game creator ({name}) and admin can do that.")
|
text=_("Only the game creator ({name}) and admin can do that.")
|
||||||
.format(name=game.starter.first_name),
|
.format(name=game.starter.first_name),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
@ -506,14 +515,14 @@ def enable_translations(bot, update):
|
||||||
|
|
||||||
|
|
||||||
@user_locale
|
@user_locale
|
||||||
def disable_translations(bot, update):
|
def disable_translations(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /disable_translations command"""
|
"""Handler for the /disable_translations command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
games = gm.chatid_games.get(chat.id)
|
games = gm.chatid_games.get(chat.id)
|
||||||
|
|
||||||
if not games:
|
if not games:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("There is no running game in this chat."))
|
text=_("There is no running game in this chat."))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -521,13 +530,13 @@ def disable_translations(bot, update):
|
||||||
|
|
||||||
if user.id in game.owner:
|
if user.id in game.owner:
|
||||||
game.translate = False
|
game.translate = False
|
||||||
send_async(bot, chat.id, text=_("Disabled multi-translations. "
|
send_async(context.bot, chat.id, text=_("Disabled multi-translations. "
|
||||||
"Enable them again with "
|
"Enable them again with "
|
||||||
"/enable_translations"))
|
"/enable_translations"))
|
||||||
return
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Only the game creator ({name}) and admin can do that.")
|
text=_("Only the game creator ({name}) and admin can do that.")
|
||||||
.format(name=game.starter.first_name),
|
.format(name=game.starter.first_name),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
|
@ -536,14 +545,14 @@ def disable_translations(bot, update):
|
||||||
|
|
||||||
@game_locales
|
@game_locales
|
||||||
@user_locale
|
@user_locale
|
||||||
def skip_player(bot, update):
|
def skip_player(update: Update, context: CallbackContext):
|
||||||
"""Handler for the /skip command"""
|
"""Handler for the /skip command"""
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
|
|
||||||
player = gm.player_for_user_in_chat(user, chat)
|
player = gm.player_for_user_in_chat(user, chat)
|
||||||
if not player:
|
if not player:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("You are not playing in a game in this chat."))
|
text=_("You are not playing in a game in this chat."))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -558,19 +567,19 @@ def skip_player(bot, update):
|
||||||
# You can skip yourself even if you have time left (you'll still draw)
|
# You can skip yourself even if you have time left (you'll still draw)
|
||||||
if delta < skipped_player.waiting_time and player != skipped_player:
|
if delta < skipped_player.waiting_time and player != skipped_player:
|
||||||
n = skipped_player.waiting_time - delta
|
n = skipped_player.waiting_time - delta
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=_("Please wait {time} second",
|
text=_("Please wait {time} second",
|
||||||
"Please wait {time} seconds",
|
"Please wait {time} seconds",
|
||||||
n)
|
n)
|
||||||
.format(time=n),
|
.format(time=n),
|
||||||
reply_to_message_id=update.message.message_id)
|
reply_to_message_id=update.message.message_id)
|
||||||
else:
|
else:
|
||||||
do_skip(bot, player)
|
do_skip(context.bot, player)
|
||||||
|
|
||||||
|
|
||||||
@game_locales
|
@game_locales
|
||||||
@user_locale
|
@user_locale
|
||||||
def reply_to_query(bot, update):
|
def reply_to_query(update: Update, context: CallbackContext):
|
||||||
"""
|
"""
|
||||||
Handler for inline queries.
|
Handler for inline queries.
|
||||||
Builds the result list for inline queries and answers to the client.
|
Builds the result list for inline queries and answers to the client.
|
||||||
|
@ -638,13 +647,13 @@ def reply_to_query(bot, update):
|
||||||
if players and game and len(players) > 1:
|
if players and game and len(players) > 1:
|
||||||
switch = _('Current game: {game}').format(game=game.chat.title)
|
switch = _('Current game: {game}').format(game=game.chat.title)
|
||||||
|
|
||||||
answer_async(bot, update.inline_query.id, results, cache_time=0,
|
answer_async(context.bot, update.inline_query.id, results, cache_time=0,
|
||||||
switch_pm_text=switch, switch_pm_parameter='select')
|
switch_pm_text=switch, switch_pm_parameter='select')
|
||||||
|
|
||||||
|
|
||||||
@game_locales
|
@game_locales
|
||||||
@user_locale
|
@user_locale
|
||||||
def process_result(bot, update, job_queue):
|
def process_result(update: Update, context: CallbackContext):
|
||||||
"""
|
"""
|
||||||
Handler for chosen inline results.
|
Handler for chosen inline results.
|
||||||
Checks the players actions and acts accordingly.
|
Checks the players actions and acts accordingly.
|
||||||
|
@ -671,38 +680,46 @@ def process_result(bot, update, job_queue):
|
||||||
mode = result_id[5:]
|
mode = result_id[5:]
|
||||||
game.set_mode(mode)
|
game.set_mode(mode)
|
||||||
logger.info("Gamemode changed to {mode}".format(mode = mode))
|
logger.info("Gamemode changed to {mode}".format(mode = mode))
|
||||||
send_async(bot, chat.id, text=__("Gamemode changed to {mode}".format(mode = mode)))
|
send_async(context.bot, chat.id, text=__("Gamemode changed to {mode}".format(mode = mode)))
|
||||||
return
|
return
|
||||||
elif len(result_id) == 36: # UUID result
|
elif len(result_id) == 36: # UUID result
|
||||||
return
|
return
|
||||||
elif int(anti_cheat) != last_anti_cheat:
|
elif int(anti_cheat) != last_anti_cheat:
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=__("Cheat attempt by {name}", multi=game.translate)
|
text=__("Cheat attempt by {name}", multi=game.translate)
|
||||||
.format(name=display_name(player.user)))
|
.format(name=display_name(player.user)))
|
||||||
return
|
return
|
||||||
elif result_id == 'call_bluff':
|
elif result_id == 'call_bluff':
|
||||||
reset_waiting_time(bot, player)
|
reset_waiting_time(context.bot, player)
|
||||||
do_call_bluff(bot, player)
|
do_call_bluff(context.bot, player)
|
||||||
elif result_id == 'draw':
|
elif result_id == 'draw':
|
||||||
reset_waiting_time(bot, player)
|
reset_waiting_time(context.bot, player)
|
||||||
do_draw(bot, player)
|
do_draw(context.bot, player)
|
||||||
elif result_id == 'pass':
|
elif result_id == 'pass':
|
||||||
game.turn()
|
game.turn()
|
||||||
elif result_id in c.COLORS:
|
elif result_id in c.COLORS:
|
||||||
game.choose_color(result_id)
|
game.choose_color(result_id)
|
||||||
else:
|
else:
|
||||||
reset_waiting_time(bot, player)
|
reset_waiting_time(context.bot, player)
|
||||||
do_play_card(bot, player, result_id)
|
if game.mode == "text":
|
||||||
|
sticker_id = c.STICKERS.get(result_id)
|
||||||
|
if sticker_id:
|
||||||
|
context.bot.sendSticker(chat.id,
|
||||||
|
sticker=sticker_id,
|
||||||
|
timeout=TIMEOUT)
|
||||||
|
else:
|
||||||
|
logger.warning(f"no sticker found for {result_id=}")
|
||||||
|
do_play_card(context.bot, player, result_id)
|
||||||
|
|
||||||
if game_is_running(game):
|
if game_is_running(game):
|
||||||
nextplayer_message = (
|
nextplayer_message = (
|
||||||
__("Next player: {name}", multi=game.translate)
|
__("Next player: {name}", multi=game.translate)
|
||||||
.format(name=display_name(game.current_player.user)))
|
.format(name=display_name(game.current_player.user)))
|
||||||
choice = [[InlineKeyboardButton(text=_("Make your choice!"), switch_inline_query_current_chat='')]]
|
choice = [[InlineKeyboardButton(text=_("Make your choice!"), switch_inline_query_current_chat='')]]
|
||||||
send_async(bot, chat.id,
|
send_async(context.bot, chat.id,
|
||||||
text=nextplayer_message,
|
text=nextplayer_message,
|
||||||
reply_markup=InlineKeyboardMarkup(choice))
|
reply_markup=InlineKeyboardMarkup(choice))
|
||||||
start_player_countdown(bot, game, job_queue)
|
start_player_countdown(context.bot, game, context.job_queue)
|
||||||
|
|
||||||
|
|
||||||
def reset_waiting_time(bot, player):
|
def reset_waiting_time(bot, player):
|
||||||
|
|
354
card.py
|
@ -60,122 +60,252 @@ DRAW_FOUR = 'draw_four'
|
||||||
|
|
||||||
SPECIALS = (CHOOSE, DRAW_FOUR)
|
SPECIALS = (CHOOSE, DRAW_FOUR)
|
||||||
|
|
||||||
|
CARDS_CLASSIC = {
|
||||||
|
"normal": {
|
||||||
|
"b_0": "BQADBAAD2QEAAl9XmQAB--inQsYcLTsC",
|
||||||
|
"b_1": "BQADBAAD2wEAAl9XmQABBzh4U-rFicEC",
|
||||||
|
"b_2": "BQADBAAD3QEAAl9XmQABo3l6TT0MzKwC",
|
||||||
|
"b_3": "BQADBAAD3wEAAl9XmQAB2y-3TSapRtIC",
|
||||||
|
"b_4": "BQADBAAD4QEAAl9XmQABT6nhOuolqKYC",
|
||||||
|
"b_5": "BQADBAAD4wEAAl9XmQABwRfmekGnpn0C",
|
||||||
|
"b_6": "BQADBAAD5QEAAl9XmQABQITgUsEsqxsC",
|
||||||
|
"b_7": "BQADBAAD5wEAAl9XmQABVhPF6EcfWjEC",
|
||||||
|
"b_8": "BQADBAAD6QEAAl9XmQABP6baig0pIvYC",
|
||||||
|
"b_9": "BQADBAAD6wEAAl9XmQAB0CQdsQs_pXIC",
|
||||||
|
"b_draw": "BQADBAAD7QEAAl9XmQAB00Wii7R3gDUC",
|
||||||
|
"b_skip": "BQADBAAD8QEAAl9XmQAB_RJHYKqlc-wC",
|
||||||
|
"b_reverse": "BQADBAAD7wEAAl9XmQABo7D0B9NUPmYC",
|
||||||
|
"g_0": "BQADBAAD9wEAAl9XmQABb8CaxxsQ-Y8C",
|
||||||
|
"g_1": "BQADBAAD-QEAAl9XmQAB9B6ti_j6UB0C",
|
||||||
|
"g_2": "BQADBAAD-wEAAl9XmQABYpLjOzbRz8EC",
|
||||||
|
"g_3": "BQADBAAD_QEAAl9XmQABKvc2ZCiY-D8C",
|
||||||
|
"g_4": "BQADBAAD_wEAAl9XmQABJB52wzPdHssC",
|
||||||
|
"g_5": "BQADBAADAQIAAl9XmQABp_Ep1I4GA2cC",
|
||||||
|
"g_6": "BQADBAADAwIAAl9XmQABaaMxxa4MihwC",
|
||||||
|
"g_7": "BQADBAADBQIAAl9XmQABv5Q264Crz8gC",
|
||||||
|
"g_8": "BQADBAADBwIAAl9XmQABjMH-X9UHh8sC",
|
||||||
|
"g_9": "BQADBAADCQIAAl9XmQAB26fZ2fW7vM0C",
|
||||||
|
"g_draw": "BQADBAADCwIAAl9XmQAB64jIZrgXrQUC",
|
||||||
|
"g_skip": "BQADBAADDwIAAl9XmQAB17yhhnh46VQC",
|
||||||
|
"g_reverse": "BQADBAADDQIAAl9XmQAB_xcaab0DkegC",
|
||||||
|
"r_0": "BQADBAADEQIAAl9XmQABiUfr1hz-zT8C",
|
||||||
|
"r_1": "BQADBAADEwIAAl9XmQAB5bWfwJGs6Q0C",
|
||||||
|
"r_2": "BQADBAADFQIAAl9XmQABHR4mg9Ifjw0C",
|
||||||
|
"r_3": "BQADBAADFwIAAl9XmQABYBx5O_PG2QIC",
|
||||||
|
"r_4": "BQADBAADGQIAAl9XmQABTQpGrlvet3cC",
|
||||||
|
"r_5": "BQADBAADGwIAAl9XmQABbdLt4gdntBQC",
|
||||||
|
"r_6": "BQADBAADHQIAAl9XmQABqEI274p3lSoC",
|
||||||
|
"r_7": "BQADBAADHwIAAl9XmQABCw8u67Q4EK4C",
|
||||||
|
"r_8": "BQADBAADIQIAAl9XmQAB8iDJmLxp8ogC",
|
||||||
|
"r_9": "BQADBAADIwIAAl9XmQAB_HCAww1kNGYC",
|
||||||
|
"r_draw": "BQADBAADJQIAAl9XmQABuz0OZ4l3k6MC",
|
||||||
|
"r_skip": "BQADBAADKQIAAl9XmQAC2AL5Ok_ULwI",
|
||||||
|
"r_reverse": "BQADBAADJwIAAl9XmQABu2tIeQTpDvUC",
|
||||||
|
"y_0": "BQADBAADKwIAAl9XmQAB_nWoNKe8DOQC",
|
||||||
|
"y_1": "BQADBAADLQIAAl9XmQABVprAGUDKgOQC",
|
||||||
|
"y_2": "BQADBAADLwIAAl9XmQABqyT4_YTm54EC",
|
||||||
|
"y_3": "BQADBAADMQIAAl9XmQABGC-Xxg_N6fIC",
|
||||||
|
"y_4": "BQADBAADMwIAAl9XmQABbc-ZGL8kApAC",
|
||||||
|
"y_5": "BQADBAADNQIAAl9XmQAB67QJZIF6XAcC",
|
||||||
|
"y_6": "BQADBAADNwIAAl9XmQABJg_7XXoITsoC",
|
||||||
|
"y_7": "BQADBAADOQIAAl9XmQABVrd7OcS2k34C",
|
||||||
|
"y_8": "BQADBAADOwIAAl9XmQABRpJSahBWk3EC",
|
||||||
|
"y_9": "BQADBAADPQIAAl9XmQAB9MwJWKLJogYC",
|
||||||
|
"y_draw": "BQADBAADPwIAAl9XmQABaPYK8oYg84cC",
|
||||||
|
"y_skip": "BQADBAADQwIAAl9XmQABO_AZKtxY6IMC",
|
||||||
|
"y_reverse": "BQADBAADQQIAAl9XmQABZdQFahGG6UQC",
|
||||||
|
"draw_four": "BQADBAAD9QEAAl9XmQABVlkSNfhn76cC",
|
||||||
|
"colorchooser": "BQADBAAD8wEAAl9XmQABl9rUOPqx4E4C",
|
||||||
|
},
|
||||||
|
"not_playable": {
|
||||||
|
"b_0": "BQADBAADRQIAAl9XmQAB1IfkQ5xAiK4C",
|
||||||
|
"b_1": "BQADBAADRwIAAl9XmQABbWvhTeKBii4C",
|
||||||
|
"b_2": "BQADBAADSQIAAl9XmQABS1djHgyQokMC",
|
||||||
|
"b_3": "BQADBAADSwIAAl9XmQABwQ6VTbgY-MIC",
|
||||||
|
"b_4": "BQADBAADTQIAAl9XmQABAlKUYha8YccC",
|
||||||
|
"b_5": "BQADBAADTwIAAl9XmQABMvx8xVDnhUEC",
|
||||||
|
"b_6": "BQADBAADUQIAAl9XmQABDEbhP1Zd31kC",
|
||||||
|
"b_7": "BQADBAADUwIAAl9XmQABXb5XQBBaAnIC",
|
||||||
|
"b_8": "BQADBAADVQIAAl9XmQABgL5HRDLvrjgC",
|
||||||
|
"b_9": "BQADBAADVwIAAl9XmQABtO3XDQWZLtYC",
|
||||||
|
"b_draw": "BQADBAADWQIAAl9XmQAB2kk__6_2IhMC",
|
||||||
|
"b_skip": "BQADBAADXQIAAl9XmQABEGJI6CaH3vcC",
|
||||||
|
"b_reverse": "BQADBAADWwIAAl9XmQAB_kZA6UdHXU8C",
|
||||||
|
"g_0": "BQADBAADYwIAAl9XmQABGD5a9oG7Yg4C",
|
||||||
|
"g_1": "BQADBAADZQIAAl9XmQABqwABZHAXZIg0Ag",
|
||||||
|
"g_2": "BQADBAADZwIAAl9XmQABTI3mrEhojRkC",
|
||||||
|
"g_3": "BQADBAADaQIAAl9XmQABVi3rUyzWS3YC",
|
||||||
|
"g_4": "BQADBAADawIAAl9XmQABZIf5ThaXnpUC",
|
||||||
|
"g_5": "BQADBAADbQIAAl9XmQABNndVJSQCenIC",
|
||||||
|
"g_6": "BQADBAADbwIAAl9XmQABpoy1c4ZkrvwC",
|
||||||
|
"g_7": "BQADBAADcQIAAl9XmQABDeaT5fzxwREC",
|
||||||
|
"g_8": "BQADBAADcwIAAl9XmQABLIQ06ZM5NnAC",
|
||||||
|
"g_9": "BQADBAADdQIAAl9XmQABel-mC7eXGsMC",
|
||||||
|
"g_draw": "BQADBAADdwIAAl9XmQABOHEpxSztCf8C",
|
||||||
|
"g_skip": "BQADBAADewIAAl9XmQABDaQdMxjjPsoC",
|
||||||
|
"g_reverse": "BQADBAADeQIAAl9XmQABek1lGz7SJNAC",
|
||||||
|
"r_0": "BQADBAADfQIAAl9XmQABWrxoiXcsg0EC",
|
||||||
|
"r_1": "BQADBAADfwIAAl9XmQABlav-bkgSgRcC",
|
||||||
|
"r_2": "BQADBAADgQIAAl9XmQABDjZkqfJ4AdAC",
|
||||||
|
"r_3": "BQADBAADgwIAAl9XmQABT7lH7VVcy3MC",
|
||||||
|
"r_4": "BQADBAADhQIAAl9XmQAB1arPC5x0LrwC",
|
||||||
|
"r_5": "BQADBAADhwIAAl9XmQABWvs7xkCDldkC",
|
||||||
|
"r_6": "BQADBAADiQIAAl9XmQABjwABH5ZonWn8Ag",
|
||||||
|
"r_7": "BQADBAADiwIAAl9XmQABjekJfm4fBDIC",
|
||||||
|
"r_8": "BQADBAADjQIAAl9XmQABqFjchpsJeEkC",
|
||||||
|
"r_9": "BQADBAADjwIAAl9XmQAB-sKdcgABdNKDAg",
|
||||||
|
"r_draw": "BQADBAADkQIAAl9XmQABtw9RPVDHZOQC",
|
||||||
|
"r_skip": "BQADBAADlQIAAl9XmQABtG2GixCxtX4C",
|
||||||
|
"r_reverse": "BQADBAADkwIAAl9XmQABz2qyEbabnVsC",
|
||||||
|
"y_0": "BQADBAADlwIAAl9XmQABAb3ZwTGS1lMC",
|
||||||
|
"y_1": "BQADBAADmQIAAl9XmQAB9v5qJk9R0x8C",
|
||||||
|
"y_2": "BQADBAADmwIAAl9XmQABCsgpRHC2g-cC",
|
||||||
|
"y_3": "BQADBAADnQIAAl9XmQAB3kLLXCv-qY0C",
|
||||||
|
"y_4": "BQADBAADnwIAAl9XmQAB7R_y-NexNLIC",
|
||||||
|
"y_5": "BQADBAADoQIAAl9XmQABl-7mwsjD-cMC",
|
||||||
|
"y_6": "BQADBAADowIAAl9XmQABwbVsyv2MfPkC",
|
||||||
|
"y_7": "BQADBAADpQIAAl9XmQABoBqC0JsemVwC",
|
||||||
|
"y_8": "BQADBAADpwIAAl9XmQABpkwAAeh9ldlHAg",
|
||||||
|
"y_9": "BQADBAADqQIAAl9XmQABpSBEUfd4IM8C",
|
||||||
|
"y_draw": "BQADBAADqwIAAl9XmQABMt-2zW0VYb4C",
|
||||||
|
"y_skip": "BQADBAADrwIAAl9XmQABIDf-_TuuxtEC",
|
||||||
|
"y_reverse": "BQADBAADrQIAAl9XmQABm9M0Zh-_UwkC",
|
||||||
|
"draw_four": "BQADBAADYQIAAl9XmQAB_HWlvZIscDEC",
|
||||||
|
"colorchooser": "BQADBAADXwIAAl9XmQABY_ksDdMex-wC",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CARDS_CLASSIC_COLORBLIND = {
|
||||||
|
"normal": {
|
||||||
|
"colorchooser": "CAADBAADrg4AAvX2mVEpx_BiDIE5nQI",
|
||||||
|
"draw_four": "CAADBAADYRAAArnkmVGmqXHhjWEBxAI",
|
||||||
|
"r_0": "CAADBAAD6A8AAn_ckVHPWHqiBR_3jAI",
|
||||||
|
"r_1": "CAADBAAD5Q0AAg-ImVEx-blQI88RrQI",
|
||||||
|
"r_2": "CAADBAAD1g0AAuMjmVEkQsVhN49DMAI",
|
||||||
|
"r_3": "CAADBAADlhAAAqy4mVHWovoaWfQG_gI",
|
||||||
|
"r_4": "CAADBAADCRoAAqf_kVFnl8ACL1rjpwI",
|
||||||
|
"r_5": "CAADBAADVw8AAjmamVEEv2TVeL9cpQI",
|
||||||
|
"r_6": "CAADBAADHQ4AAuuUkVH2I-yn6nRBVAI",
|
||||||
|
"r_7": "CAADBAADNQ8AArP1kVF5rqHtk0pQ-AI",
|
||||||
|
"r_8": "CAADBAAD1BAAAuQDkVEPiIodUi6WvwI",
|
||||||
|
"r_9": "CAADBAAD2Q4AAq1nkFHM6z5C0Kff2QI",
|
||||||
|
"r_draw": "CAADBAADvQ8AAqZukFGEmkRSoSZQEwI",
|
||||||
|
"r_reverse": "CAADBAAD5RAAAg89mVE8-EY_2DifcAI",
|
||||||
|
"r_skip": "CAADBAADRg4AAp8bmVFOC6xdEZZRwwI",
|
||||||
|
"g_0": "CAADBAADTg4AAoQxmFF07jR_vfB4xgI",
|
||||||
|
"g_1": "CAADBAADQg4AAhkgmFGlsif9nNtXwgI",
|
||||||
|
"g_2": "CAADBAAD2BUAAue_mFGENiPSjZxbiQI",
|
||||||
|
"g_3": "CAADBAADpw4AAjO9mFHAOz8KD2n7BwI",
|
||||||
|
"g_4": "CAADBAADRhAAAqF7kFEcwLalLfDfaAI",
|
||||||
|
"g_5": "CAADBAADAg8AAqXLmFHJyg2F_ybbvwI",
|
||||||
|
"g_6": "CAADBAADVhYAAtK7mVGigRq_EkCuVgI",
|
||||||
|
"g_7": "CAADBAAD2RIAArccmFEj-8LIVNAbsgI",
|
||||||
|
"g_8": "CAADBAAD6AwAAuvmmFHBRarMimOWawI",
|
||||||
|
"g_9": "CAADBAADExEAAsNkmVFr8DaHGOwsggI",
|
||||||
|
"g_draw": "CAADBAADhA8AArxYmVH9ch5Jp00AAboC",
|
||||||
|
"g_reverse": "CAADBAADMhAAAvVOmFGH284LIY7cegI",
|
||||||
|
"g_skip": "CAADBAADbBcAAqinkVEOwkJtDRfk2gI",
|
||||||
|
"b_0": "CAADBAAD-BAAAkj8kFG61GJdw29QOAI",
|
||||||
|
"b_1": "CAADBAADcRMAAu-EmFFT1i4LcqO4OQI",
|
||||||
|
"b_2": "CAADBAAD0xQAAqVhmVHyrFSAbxtfjwI",
|
||||||
|
"b_3": "CAADBAADNg0AAn-xmFHev8IdF_ie0wI",
|
||||||
|
"b_4": "CAADBAADlQ4AAjZamVFcIL_pVB5cFwI",
|
||||||
|
"b_5": "CAADBAADrgwAAuL5mVHvEBZ8CG5p5QI",
|
||||||
|
"b_6": "CAADBAADDhUAAuGRmVGQYvmEOxczBAI",
|
||||||
|
"b_7": "CAADBAADIxEAAv_dmFEuVt39kkgZgwI",
|
||||||
|
"b_8": "CAADBAAD2w0AAoE6kVHG7WscV4F2hwI",
|
||||||
|
"b_9": "CAADBAADvQ0AArRMmVErWaSRP_giKQI",
|
||||||
|
"b_draw": "CAADBAADlw4AAjF_kFHPWSoYKBwtwQI",
|
||||||
|
"b_reverse": "CAADBAADog8AAqDJmVEJQp5WocnUnQI",
|
||||||
|
"b_skip": "CAADBAAD-QwAAgbZmFGltUlnslDNUQI",
|
||||||
|
"y_0": "CAADBAADrQ4AAr5WmVHNf69eBn2YOAI",
|
||||||
|
"y_1": "CAADBAADcg8AAmqKmVHfVeUI3u_i7AI",
|
||||||
|
"y_2": "CAADBAADkA4AAuDImFEQ8qjFlcKplQI",
|
||||||
|
"y_3": "CAADBAAD-QwAAmromFGAqVn-Y8N72wI",
|
||||||
|
"y_4": "CAADBAADjQ4AAmNLmFG80k7kfgx1NAI",
|
||||||
|
"y_5": "CAADBAADqQ8AAmgYmFH1_ey_bMQNYwI",
|
||||||
|
"y_6": "CAADBAADdQ0AAuWcmFEbG_gm1wGYCQI",
|
||||||
|
"y_7": "CAADBAAD6QwAApQAAZhRI8OfRvLX3vkC",
|
||||||
|
"y_8": "CAADBAADARAAAi-2kVEifJ-O9WVilgI",
|
||||||
|
"y_9": "CAADBAADxA0AAhQ8mFHjnl9tUCHSLAI",
|
||||||
|
"y_draw": "CAADBAADzw4AAncZmVEhLhX17eqX8AI",
|
||||||
|
"y_reverse": "CAADBAADTxAAAqgFmVEJRBw4eWgnDwI",
|
||||||
|
"y_skip": "CAADBAADPhYAAiGbkFG9hptFPLgj7wI",
|
||||||
|
},
|
||||||
|
"not_playable": {
|
||||||
|
"colorchooser": "CAADBAADpQ4AAlfDmFFHGkwyGFeCFQI",
|
||||||
|
"draw_four": "CAADBAADMRMAAv7amFHvKGLoNyFbNQI",
|
||||||
|
"r_0": "CAADBAADsBMAAuGdkFHTZ-jl4eNn-gI",
|
||||||
|
"r_1": "CAADBAADVA4AAhpfkFEKt19qveGSPgI",
|
||||||
|
"r_2": "CAADBAADrw0AAoWsmVHguULNoYJwUwI",
|
||||||
|
"r_3": "CAADBAADzxMAAjvkkFFdtKJu5WGwUgI",
|
||||||
|
"r_4": "CAADBAAD1Q8AAoHZkFFvyQnFHzfwiQI",
|
||||||
|
"r_5": "CAADBAADWxEAAvkHkFGUo86qxKV0kwI",
|
||||||
|
"r_6": "CAADBAAD_hIAAjx0mVGmlm-b_FHQBAI",
|
||||||
|
"r_7": "CAADBAADmhEAAslomFHOv7bqcDJkDAI",
|
||||||
|
"r_8": "CAADBAADtw0AAgqVmVG2HdSbcJYxZgI",
|
||||||
|
"r_9": "CAADBAADNxEAAuF6mVE3WzTMJkSVAgI",
|
||||||
|
"r_draw": "CAADBAADVxAAAiNukFE1K2xORNnfMwI",
|
||||||
|
"r_reverse": "CAADBAADQxMAAvH0mVHKznpt-uu9ngI",
|
||||||
|
"r_skip": "CAADBAADZA4AApbPkFFB9E2Px-HFpgI",
|
||||||
|
"g_0": "CAADBAAD8w4AAjDEmFG7DwKggUEj9QI",
|
||||||
|
"g_1": "CAADBAAD2g0AAo_DmVHIPG84WdIo1wI",
|
||||||
|
"g_2": "CAADBAADEhEAAoRXmVGIG2nuN45P6AI",
|
||||||
|
"g_3": "CAADBAADug8AAsSRmFFzk0TcRuG8VAI",
|
||||||
|
"g_4": "CAADBAADrQ8AAvgmkFESfo9BjF7-3gI",
|
||||||
|
"g_5": "CAADBAADVhAAAnPqkFFtxtFX9HlT-AI",
|
||||||
|
"g_6": "CAADBAADMg8AAiSBmFHIQw1jFjv6UwI",
|
||||||
|
"g_7": "CAADBAADvREAAv0BkVGDq3H1DCq_DQI",
|
||||||
|
"g_8": "CAADBAADWQ4AAhOEkVG96JDgCtFrEwI",
|
||||||
|
"g_9": "CAADBAAD2xYAAruDmFFAUMFryEwjoAI",
|
||||||
|
"g_draw": "CAADBAADLA4AAu9tkVGTzBbeeYydIQI",
|
||||||
|
"g_reverse": "CAADBAADVAwAAhYYmFExJS0ozE8-rAI",
|
||||||
|
"g_skip": "CAADBAADYg4AAulsmFHxOkaz9OsTiwI",
|
||||||
|
"b_0": "CAADBAADVxUAAtnOkFEIAAGw5CZEIxgC",
|
||||||
|
"b_1": "CAADBAAD1RAAAnQqkFF9kDqD0wp3ngI",
|
||||||
|
"b_2": "CAADBAADZg4AAvcUmVHTXwldirf1hAI",
|
||||||
|
"b_3": "CAADBAADfBAAAkX1mVHw0CWX0h31iQI",
|
||||||
|
"b_4": "CAADBAADPBAAAuTCmFFDpvXzes4qjwI",
|
||||||
|
"b_5": "CAADBAADTQ4AAsWQmVHcrxDQUWOB4AI",
|
||||||
|
"b_6": "CAADBAAD_hAAAoUhmVG8kjd65J8EngI",
|
||||||
|
"b_7": "CAADBAADlRAAArtjkFGko5TuFNnncwI",
|
||||||
|
"b_8": "CAADBAADZQ8AAltEmFE_fDYIXBrV3QI",
|
||||||
|
"b_9": "CAADBAADrhAAAtM-mVGwhrWTD9IaYgI",
|
||||||
|
"b_draw": "CAADBAADtQ0AAnVbmFGC1hI60JaOQQI",
|
||||||
|
"b_reverse": "CAADBAADShEAAlcOmFHStPeFzfVIEwI",
|
||||||
|
"b_skip": "CAADBAAD_xEAAgZFmVFMRA1J8Y1gxAI",
|
||||||
|
"y_0": "CAADBAAD7xAAAqjjmFHnCu7eKJvSBgI",
|
||||||
|
"y_1": "CAADBAADJQwAAp6tmFE2zDPVMieQ2QI",
|
||||||
|
"y_2": "CAADBAADNA4AAl2mmVFpQOxJ41gk_gI",
|
||||||
|
"y_3": "CAADBAAD3A4AAsxPmFGyZFv42UlxAQI",
|
||||||
|
"y_4": "CAADBAADwg8AAm88kVEc9HZpl2gmzQI",
|
||||||
|
"y_5": "CAADBAAD5hIAAkQ6mFHS-aGVuYZAnAI",
|
||||||
|
"y_6": "CAADBAADvQ8AAs3RmVHVkVBfEF7eIwI",
|
||||||
|
"y_7": "CAADBAAD1gwAAjlbmFGGH6rBdqP8QQI",
|
||||||
|
"y_8": "CAADBAADbg8AAqvXkVH1ESeZFcGVrgI",
|
||||||
|
"y_9": "CAADBAADOQ8AAnjokVG96pmCP7aZ3AI",
|
||||||
|
"y_draw": "CAADBAAD6w4AAgsJmVETUteFwqTVJgI",
|
||||||
|
"y_reverse": "CAADBAADtg8AAqiFmFFwothyN9TrXwI",
|
||||||
|
"y_skip": "CAADBAADSxEAAhcSmFGu_F5LffmsZgI",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
STICKERS_OPTIONS = {
|
||||||
|
"option_draw": "BQADBAAD-AIAAl9XmQABxEjEcFM-VHIC",
|
||||||
|
"option_pass": "BQADBAAD-gIAAl9XmQABcEkAAbaZ4SicAg",
|
||||||
|
"option_bluff": "BQADBAADygIAAl9XmQABJoLfB9ntI2UC",
|
||||||
|
"option_info": "BQADBAADxAIAAl9XmQABC5v3Z77VLfEC",
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: Support multiple card packs
|
||||||
|
# For now, just use classic colorblind
|
||||||
STICKERS = {
|
STICKERS = {
|
||||||
'b_0': 'BQADBAAD2QEAAl9XmQAB--inQsYcLTsC',
|
**CARDS_CLASSIC_COLORBLIND["normal"],
|
||||||
'b_1': 'BQADBAAD2wEAAl9XmQABBzh4U-rFicEC',
|
**STICKERS_OPTIONS,
|
||||||
'b_2': 'BQADBAAD3QEAAl9XmQABo3l6TT0MzKwC',
|
|
||||||
'b_3': 'BQADBAAD3wEAAl9XmQAB2y-3TSapRtIC',
|
|
||||||
'b_4': 'BQADBAAD4QEAAl9XmQABT6nhOuolqKYC',
|
|
||||||
'b_5': 'BQADBAAD4wEAAl9XmQABwRfmekGnpn0C',
|
|
||||||
'b_6': 'BQADBAAD5QEAAl9XmQABQITgUsEsqxsC',
|
|
||||||
'b_7': 'BQADBAAD5wEAAl9XmQABVhPF6EcfWjEC',
|
|
||||||
'b_8': 'BQADBAAD6QEAAl9XmQABP6baig0pIvYC',
|
|
||||||
'b_9': 'BQADBAAD6wEAAl9XmQAB0CQdsQs_pXIC',
|
|
||||||
'b_draw': 'BQADBAAD7QEAAl9XmQAB00Wii7R3gDUC',
|
|
||||||
'b_skip': 'BQADBAAD8QEAAl9XmQAB_RJHYKqlc-wC',
|
|
||||||
'b_reverse': 'BQADBAAD7wEAAl9XmQABo7D0B9NUPmYC',
|
|
||||||
'g_0': 'BQADBAAD9wEAAl9XmQABb8CaxxsQ-Y8C',
|
|
||||||
'g_1': 'BQADBAAD-QEAAl9XmQAB9B6ti_j6UB0C',
|
|
||||||
'g_2': 'BQADBAAD-wEAAl9XmQABYpLjOzbRz8EC',
|
|
||||||
'g_3': 'BQADBAAD_QEAAl9XmQABKvc2ZCiY-D8C',
|
|
||||||
'g_4': 'BQADBAAD_wEAAl9XmQABJB52wzPdHssC',
|
|
||||||
'g_5': 'BQADBAADAQIAAl9XmQABp_Ep1I4GA2cC',
|
|
||||||
'g_6': 'BQADBAADAwIAAl9XmQABaaMxxa4MihwC',
|
|
||||||
'g_7': 'BQADBAADBQIAAl9XmQABv5Q264Crz8gC',
|
|
||||||
'g_8': 'BQADBAADBwIAAl9XmQABjMH-X9UHh8sC',
|
|
||||||
'g_9': 'BQADBAADCQIAAl9XmQAB26fZ2fW7vM0C',
|
|
||||||
'g_draw': 'BQADBAADCwIAAl9XmQAB64jIZrgXrQUC',
|
|
||||||
'g_skip': 'BQADBAADDwIAAl9XmQAB17yhhnh46VQC',
|
|
||||||
'g_reverse': 'BQADBAADDQIAAl9XmQAB_xcaab0DkegC',
|
|
||||||
'r_0': 'BQADBAADEQIAAl9XmQABiUfr1hz-zT8C',
|
|
||||||
'r_1': 'BQADBAADEwIAAl9XmQAB5bWfwJGs6Q0C',
|
|
||||||
'r_2': 'BQADBAADFQIAAl9XmQABHR4mg9Ifjw0C',
|
|
||||||
'r_3': 'BQADBAADFwIAAl9XmQABYBx5O_PG2QIC',
|
|
||||||
'r_4': 'BQADBAADGQIAAl9XmQABTQpGrlvet3cC',
|
|
||||||
'r_5': 'BQADBAADGwIAAl9XmQABbdLt4gdntBQC',
|
|
||||||
'r_6': 'BQADBAADHQIAAl9XmQABqEI274p3lSoC',
|
|
||||||
'r_7': 'BQADBAADHwIAAl9XmQABCw8u67Q4EK4C',
|
|
||||||
'r_8': 'BQADBAADIQIAAl9XmQAB8iDJmLxp8ogC',
|
|
||||||
'r_9': 'BQADBAADIwIAAl9XmQAB_HCAww1kNGYC',
|
|
||||||
'r_draw': 'BQADBAADJQIAAl9XmQABuz0OZ4l3k6MC',
|
|
||||||
'r_skip': 'BQADBAADKQIAAl9XmQAC2AL5Ok_ULwI',
|
|
||||||
'r_reverse': 'BQADBAADJwIAAl9XmQABu2tIeQTpDvUC',
|
|
||||||
'y_0': 'BQADBAADKwIAAl9XmQAB_nWoNKe8DOQC',
|
|
||||||
'y_1': 'BQADBAADLQIAAl9XmQABVprAGUDKgOQC',
|
|
||||||
'y_2': 'BQADBAADLwIAAl9XmQABqyT4_YTm54EC',
|
|
||||||
'y_3': 'BQADBAADMQIAAl9XmQABGC-Xxg_N6fIC',
|
|
||||||
'y_4': 'BQADBAADMwIAAl9XmQABbc-ZGL8kApAC',
|
|
||||||
'y_5': 'BQADBAADNQIAAl9XmQAB67QJZIF6XAcC',
|
|
||||||
'y_6': 'BQADBAADNwIAAl9XmQABJg_7XXoITsoC',
|
|
||||||
'y_7': 'BQADBAADOQIAAl9XmQABVrd7OcS2k34C',
|
|
||||||
'y_8': 'BQADBAADOwIAAl9XmQABRpJSahBWk3EC',
|
|
||||||
'y_9': 'BQADBAADPQIAAl9XmQAB9MwJWKLJogYC',
|
|
||||||
'y_draw': 'BQADBAADPwIAAl9XmQABaPYK8oYg84cC',
|
|
||||||
'y_skip': 'BQADBAADQwIAAl9XmQABO_AZKtxY6IMC',
|
|
||||||
'y_reverse': 'BQADBAADQQIAAl9XmQABZdQFahGG6UQC',
|
|
||||||
'draw_four': 'BQADBAAD9QEAAl9XmQABVlkSNfhn76cC',
|
|
||||||
'colorchooser': 'BQADBAAD8wEAAl9XmQABl9rUOPqx4E4C',
|
|
||||||
'option_draw': 'BQADBAAD-AIAAl9XmQABxEjEcFM-VHIC',
|
|
||||||
'option_pass': 'BQADBAAD-gIAAl9XmQABcEkAAbaZ4SicAg',
|
|
||||||
'option_bluff': 'BQADBAADygIAAl9XmQABJoLfB9ntI2UC',
|
|
||||||
'option_info': 'BQADBAADxAIAAl9XmQABC5v3Z77VLfEC'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STICKERS_GREY = {
|
STICKERS_GREY = {
|
||||||
'b_0': 'BQADBAADRQIAAl9XmQAB1IfkQ5xAiK4C',
|
**CARDS_CLASSIC_COLORBLIND["not_playable"],
|
||||||
'b_1': 'BQADBAADRwIAAl9XmQABbWvhTeKBii4C',
|
|
||||||
'b_2': 'BQADBAADSQIAAl9XmQABS1djHgyQokMC',
|
|
||||||
'b_3': 'BQADBAADSwIAAl9XmQABwQ6VTbgY-MIC',
|
|
||||||
'b_4': 'BQADBAADTQIAAl9XmQABAlKUYha8YccC',
|
|
||||||
'b_5': 'BQADBAADTwIAAl9XmQABMvx8xVDnhUEC',
|
|
||||||
'b_6': 'BQADBAADUQIAAl9XmQABDEbhP1Zd31kC',
|
|
||||||
'b_7': 'BQADBAADUwIAAl9XmQABXb5XQBBaAnIC',
|
|
||||||
'b_8': 'BQADBAADVQIAAl9XmQABgL5HRDLvrjgC',
|
|
||||||
'b_9': 'BQADBAADVwIAAl9XmQABtO3XDQWZLtYC',
|
|
||||||
'b_draw': 'BQADBAADWQIAAl9XmQAB2kk__6_2IhMC',
|
|
||||||
'b_skip': 'BQADBAADXQIAAl9XmQABEGJI6CaH3vcC',
|
|
||||||
'b_reverse': 'BQADBAADWwIAAl9XmQAB_kZA6UdHXU8C',
|
|
||||||
'g_0': 'BQADBAADYwIAAl9XmQABGD5a9oG7Yg4C',
|
|
||||||
'g_1': 'BQADBAADZQIAAl9XmQABqwABZHAXZIg0Ag',
|
|
||||||
'g_2': 'BQADBAADZwIAAl9XmQABTI3mrEhojRkC',
|
|
||||||
'g_3': 'BQADBAADaQIAAl9XmQABVi3rUyzWS3YC',
|
|
||||||
'g_4': 'BQADBAADawIAAl9XmQABZIf5ThaXnpUC',
|
|
||||||
'g_5': 'BQADBAADbQIAAl9XmQABNndVJSQCenIC',
|
|
||||||
'g_6': 'BQADBAADbwIAAl9XmQABpoy1c4ZkrvwC',
|
|
||||||
'g_7': 'BQADBAADcQIAAl9XmQABDeaT5fzxwREC',
|
|
||||||
'g_8': 'BQADBAADcwIAAl9XmQABLIQ06ZM5NnAC',
|
|
||||||
'g_9': 'BQADBAADdQIAAl9XmQABel-mC7eXGsMC',
|
|
||||||
'g_draw': 'BQADBAADdwIAAl9XmQABOHEpxSztCf8C',
|
|
||||||
'g_skip': 'BQADBAADewIAAl9XmQABDaQdMxjjPsoC',
|
|
||||||
'g_reverse': 'BQADBAADeQIAAl9XmQABek1lGz7SJNAC',
|
|
||||||
'r_0': 'BQADBAADfQIAAl9XmQABWrxoiXcsg0EC',
|
|
||||||
'r_1': 'BQADBAADfwIAAl9XmQABlav-bkgSgRcC',
|
|
||||||
'r_2': 'BQADBAADgQIAAl9XmQABDjZkqfJ4AdAC',
|
|
||||||
'r_3': 'BQADBAADgwIAAl9XmQABT7lH7VVcy3MC',
|
|
||||||
'r_4': 'BQADBAADhQIAAl9XmQAB1arPC5x0LrwC',
|
|
||||||
'r_5': 'BQADBAADhwIAAl9XmQABWvs7xkCDldkC',
|
|
||||||
'r_6': 'BQADBAADiQIAAl9XmQABjwABH5ZonWn8Ag',
|
|
||||||
'r_7': 'BQADBAADiwIAAl9XmQABjekJfm4fBDIC',
|
|
||||||
'r_8': 'BQADBAADjQIAAl9XmQABqFjchpsJeEkC',
|
|
||||||
'r_9': 'BQADBAADjwIAAl9XmQAB-sKdcgABdNKDAg',
|
|
||||||
'r_draw': 'BQADBAADkQIAAl9XmQABtw9RPVDHZOQC',
|
|
||||||
'r_skip': 'BQADBAADlQIAAl9XmQABtG2GixCxtX4C',
|
|
||||||
'r_reverse': 'BQADBAADkwIAAl9XmQABz2qyEbabnVsC',
|
|
||||||
'y_0': 'BQADBAADlwIAAl9XmQABAb3ZwTGS1lMC',
|
|
||||||
'y_1': 'BQADBAADmQIAAl9XmQAB9v5qJk9R0x8C',
|
|
||||||
'y_2': 'BQADBAADmwIAAl9XmQABCsgpRHC2g-cC',
|
|
||||||
'y_3': 'BQADBAADnQIAAl9XmQAB3kLLXCv-qY0C',
|
|
||||||
'y_4': 'BQADBAADnwIAAl9XmQAB7R_y-NexNLIC',
|
|
||||||
'y_5': 'BQADBAADoQIAAl9XmQABl-7mwsjD-cMC',
|
|
||||||
'y_6': 'BQADBAADowIAAl9XmQABwbVsyv2MfPkC',
|
|
||||||
'y_7': 'BQADBAADpQIAAl9XmQABoBqC0JsemVwC',
|
|
||||||
'y_8': 'BQADBAADpwIAAl9XmQABpkwAAeh9ldlHAg',
|
|
||||||
'y_9': 'BQADBAADqQIAAl9XmQABpSBEUfd4IM8C',
|
|
||||||
'y_draw': 'BQADBAADqwIAAl9XmQABMt-2zW0VYb4C',
|
|
||||||
'y_skip': 'BQADBAADrwIAAl9XmQABIDf-_TuuxtEC',
|
|
||||||
'y_reverse': 'BQADBAADrQIAAl9XmQABm9M0Zh-_UwkC',
|
|
||||||
'draw_four': 'BQADBAADYQIAAl9XmQAB_HWlvZIscDEC',
|
|
||||||
'colorchooser': 'BQADBAADXwIAAl9XmQABY_ksDdMex-wC'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
8
docker-compose.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
unobot:
|
||||||
|
container_name: unobot
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: unless-stopped
|
4
game.py
|
@ -58,7 +58,7 @@ class Game(object):
|
||||||
current_player = self.current_player
|
current_player = self.current_player
|
||||||
itplayer = current_player.next
|
itplayer = current_player.next
|
||||||
players.append(current_player)
|
players.append(current_player)
|
||||||
while itplayer and itplayer is not current_player:
|
while itplayer and itplayer != current_player:
|
||||||
players.append(itplayer)
|
players.append(itplayer)
|
||||||
itplayer = itplayer.next
|
itplayer = itplayer.next
|
||||||
return players
|
return players
|
||||||
|
@ -121,7 +121,7 @@ class Game(object):
|
||||||
self.logger.debug("Draw counter increased by 2")
|
self.logger.debug("Draw counter increased by 2")
|
||||||
elif card.value == c.REVERSE:
|
elif card.value == c.REVERSE:
|
||||||
# Special rule for two players
|
# Special rule for two players
|
||||||
if self.current_player is self.current_player.next.next:
|
if self.current_player == self.current_player.next.next:
|
||||||
self.turn()
|
self.turn()
|
||||||
else:
|
else:
|
||||||
self.reverse()
|
self.reverse()
|
||||||
|
|
|
@ -110,7 +110,7 @@ class GameManager(object):
|
||||||
for g in games:
|
for g in games:
|
||||||
for p in g.players:
|
for p in g.players:
|
||||||
if p.user.id == user.id:
|
if p.user.id == user.id:
|
||||||
if p is g.current_player:
|
if p == g.current_player:
|
||||||
g.turn()
|
g.turn()
|
||||||
|
|
||||||
p.leave()
|
p.leave()
|
||||||
|
|
4
images/api_auth.json.sample
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"api_id": 0,
|
||||||
|
"api_hash": ""
|
||||||
|
}
|
92
images/build_classic_colorblind_deck.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
"""
|
||||||
|
Script to build the classic colorblind deck from the classic deck.
|
||||||
|
|
||||||
|
Requires imagemagick to be installed and in the path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import copyfile
|
||||||
|
from subprocess import run
|
||||||
|
|
||||||
|
IMAGES_DIR = Path(__file__).resolve().parent
|
||||||
|
CLASSIC_DIR = IMAGES_DIR / "classic"
|
||||||
|
COLORBLIND_DIR = IMAGES_DIR / "classic_colorblind"
|
||||||
|
COLORBLIND_OVERLAY_DIR = IMAGES_DIR / "colorblind_overlay"
|
||||||
|
|
||||||
|
COLORS = ["r", "g", "b", "y"]
|
||||||
|
NUMBERS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "draw", "reverse", "skip"]
|
||||||
|
SPECIALS = ["colorchooser", "draw_four"]
|
||||||
|
|
||||||
|
|
||||||
|
def overlay_image(color, number):
|
||||||
|
base = CLASSIC_DIR / "png" / f"{color}_{number}.png"
|
||||||
|
overlay = COLORBLIND_OVERLAY_DIR / f"{color}.png"
|
||||||
|
out = COLORBLIND_DIR / "png" / f"{color}_{number}.png"
|
||||||
|
run(["magick", "convert", str(base), str(overlay), "-composite", str(out)])
|
||||||
|
|
||||||
|
|
||||||
|
def create_not_playable(card):
|
||||||
|
base = COLORBLIND_DIR / "png" / f"{card}.png"
|
||||||
|
overlay = COLORBLIND_OVERLAY_DIR / "not_playable.png"
|
||||||
|
out = COLORBLIND_DIR / "png_not_playable" / f"{card}.png"
|
||||||
|
|
||||||
|
run(
|
||||||
|
[
|
||||||
|
"magick",
|
||||||
|
"convert",
|
||||||
|
str(base),
|
||||||
|
"-modulate",
|
||||||
|
"75,20",
|
||||||
|
"-brightness-contrast",
|
||||||
|
"0x10",
|
||||||
|
str(overlay),
|
||||||
|
"-composite",
|
||||||
|
str(out),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_png_to_webp(suffix):
|
||||||
|
for color in COLORS:
|
||||||
|
for number in NUMBERS:
|
||||||
|
card = f"{color}_{number}"
|
||||||
|
png = COLORBLIND_DIR / f"png{suffix}" / f"{card}.png"
|
||||||
|
webp = COLORBLIND_DIR / f"webp{suffix}" / f"{card}.webp"
|
||||||
|
run(["magick", "convert", str(png), "-define", "webp:lossless=true", str(webp)])
|
||||||
|
|
||||||
|
for special in SPECIALS:
|
||||||
|
png = COLORBLIND_DIR / f"png{suffix}" / f"{special}.png"
|
||||||
|
webp = COLORBLIND_DIR / f"webp{suffix}" / f"{special}.webp"
|
||||||
|
run(["magick", "convert", str(png), "-define", "webp:lossless=true", str(webp)])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
(COLORBLIND_DIR / "png").mkdir(parents=True, exist_ok=True)
|
||||||
|
(COLORBLIND_DIR / "png_not_playable").mkdir(parents=True, exist_ok=True)
|
||||||
|
(COLORBLIND_DIR / "webp").mkdir(parents=True, exist_ok=True)
|
||||||
|
(COLORBLIND_DIR / "webp_not_playable").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for color in COLORS:
|
||||||
|
for number in NUMBERS:
|
||||||
|
overlay_image(color, number)
|
||||||
|
|
||||||
|
for special in SPECIALS:
|
||||||
|
copyfile(
|
||||||
|
CLASSIC_DIR / "png" / f"{special}.png",
|
||||||
|
COLORBLIND_DIR / "png" / f"{special}.png",
|
||||||
|
)
|
||||||
|
|
||||||
|
for color in COLORS:
|
||||||
|
for number in NUMBERS:
|
||||||
|
create_not_playable(f"{color}_{number}")
|
||||||
|
|
||||||
|
for special in SPECIALS:
|
||||||
|
create_not_playable(special)
|
||||||
|
|
||||||
|
convert_png_to_webp("")
|
||||||
|
convert_png_to_webp("_not_playable")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |