├── .dockerignore
├── LICENSE
├── README.md
├── TODO
├── docker-compose.yml
├── dockerfiles
├── nginx
│ └── Dockerfile
├── postgres
│ └── Dockerfile
├── telegram
│ └── Dockerfile
└── web
│ └── Dockerfile
├── nginx
├── nginx.conf
└── static
│ ├── css
│ ├── authorization_form.css
│ ├── base.css
│ ├── chessboards.css
│ ├── error_page.css
│ ├── fonts.css
│ ├── reset.css
│ └── success_authorization.css
│ ├── favicon.ico
│ ├── fonts
│ ├── Inter-VariableFont_slnt,wght.ttf
│ └── PlacidArmor-Medium.ttf
│ ├── images
│ ├── 1x1.png
│ ├── 45.gif
│ ├── 45_1.gif
│ ├── 45_3.gif
│ ├── 45_reversed.gif
│ ├── Avatar.png
│ ├── Icon LeftGive up.png
│ ├── Icon LeftRevenge.png
│ ├── Icon Leftdraw.png
│ ├── US_menu_icon_flag.png
│ ├── bb.png
│ ├── bk.png
│ ├── bn.png
│ ├── bp.png
│ ├── bq.png
│ ├── br.png
│ ├── burgermenu_icon.png
│ ├── error.png
│ ├── success_authorization.png
│ ├── unknown_user.png
│ ├── wb.png
│ ├── wk.png
│ ├── wn.png
│ ├── wp.png
│ ├── wq.png
│ └── wr.png
│ └── js
│ ├── authorization_response.js
│ ├── chessboard.js
│ ├── chessboard_spectator.js
│ ├── config.js
│ ├── core.js
│ ├── jquery-1.7.1.min.js
│ ├── jquery.chess.js
│ ├── jquery.chess.spectator.js
│ ├── jquery.ui.core.js
│ ├── jquery.ui.draggable.js
│ ├── jquery.ui.droppable.js
│ ├── jquery.ui.mouse.js
│ ├── jquery.ui.touch-punch.min.js
│ ├── jquery.ui.widget.js
│ ├── player_timers.js
│ ├── prepare_time.js
│ ├── websocket.js
│ └── websocket_spectator.js
├── previews
├── achievement_1
├── game
└── game_ready
├── telegram
├── QueueClass.py
├── __init__.py
├── authorization_core.py
├── config
│ ├── ConfigValues.py
│ └── config.ini
├── decorators.py
├── main.py
├── requirements.txt
├── telegram_admin.py
├── telegram_authorize.py
├── telegram_dashboard.py
├── telegram_help.py
├── telegram_log
│ └── log.py
├── telegram_profile.py
├── telegram_queue.py
└── telegram_start.py
└── web_django
├── .env
├── __init__.py
├── api
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── responses.py
├── serializers.py
├── tests.py
├── urls.py
├── utils.py
└── views.py
├── authorization
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── templates
│ └── authorization
│ │ └── success_authorization.html
├── tests.py
├── urls.py
└── views.py
├── base
└── base.html
├── blacklist
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
├── urls.py
└── views.py
├── chessboards
├── __init__.py
├── admin.py
├── apps.py
├── chess_core
│ ├── Game.py
│ ├── Timer.py
│ ├── User.py
│ ├── __init__.py
│ └── core.py
├── consumers.py
├── games_management.py
├── migrations
│ └── __init__.py
├── models.py
├── routing.py
├── templates
│ └── chessboards
│ │ ├── game.html
│ │ └── game_spectator.html
├── tests.py
├── urls.py
└── views.py
├── django_log
└── log.py
├── error_page
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── templates
│ └── error_page
│ │ └── index.html
├── tests.py
└── views.py
├── manage.py
├── requirements.txt
└── web_django
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | venv
2 | __pycache__
3 | .git
4 | .idea
5 | .gitignore
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Justiks
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
♟ CHESSBOT - БОТ ДЛЯ ШАХМАТ ♟
2 |
3 | Шамхматный бот - Это не просто очередная реализация шахмат. Вам не нужно скачивать никаких приложений, для того чтобы играть достаточно иметь аккаунт Telegram и выход в интернет, конечно ;). Это удобно при проведении шахматных турниров в ваших Telegram каналах.
4 |
5 | 
6 | 
7 |
8 | ------------------------------------------
9 | ### Releases
10 | * ##### 29.09.23 - 27.10.23 - Первый релиз для студентов Кода Будущего от 1С, поток 2022-2023
11 | * ##### 15.07.24 - 29.07.24 - Второй релиз для студентов Кода Будущего от 1С, поток 2023-2024
12 |
13 | ---------
14 | ### Ahievements
15 | * ##### Проект упомянут в презентации-отчете о проектах учеников Кода Будущего
16 | 
17 |
18 | ### Dependencies
19 | * ##### [requirements.txt](https://github.com/Justiks1337/ChessBot/blob/main/requirements.txt)
20 | * ##### https://github.com/furf/jquery-ui-touch-punch
21 | * ##### https://github.com/serbanghita/jQuery-Chess?ysclid=lm7zyb365a478152142
22 |
23 | ---------
24 | ### Installation
25 |
26 | Понадобится установленный docker и git
27 |
28 | ##### windows/mac/linux
29 | ```
30 | git clone https://github.com/Justiks1337/ChessBot/
31 | cd ChessBot
32 | docker compose up --build
33 | ```
34 |
35 | Made by Maksim Dubkov with aiogram, django and a lot of ❤️.
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 |
2 | 2. Isolate telegram bot and web site containers
3 |
4 | 5. isolate configs
5 | django has special env lib!
6 |
7 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # docker-compose.yml
2 | version: '3'
3 |
4 | services:
5 |
6 | nginx:
7 | build:
8 | context: .
9 | dockerfile: ./dockerfiles/nginx/Dockerfile
10 | depends_on:
11 | - daphne
12 | ports:
13 | - 80:80
14 | volumes:
15 | - avatars:/static/avatars/
16 |
17 | database:
18 | build:
19 | context: .
20 | dockerfile: ./dockerfiles/postgres/Dockerfile
21 | environment:
22 | POSTGRES_DB: "chess_database"
23 | POSTGRES_USER: "chess"
24 | POSTGRES_PASSWORD: "12345678"
25 | expose:
26 | - 5432
27 |
28 | daphne:
29 | build:
30 | context: .
31 | dockerfile: ./dockerfiles/web/Dockerfile
32 | tty: true
33 | command: python3 -u -m daphne web_django.asgi:application -b daphne -p 8000
34 | depends_on:
35 | - database
36 | restart: always
37 | expose:
38 | - 8000
39 | volumes:
40 | - avatars:/static/avatars/
41 |
42 |
43 | telegram:
44 | build:
45 | context: .
46 | dockerfile: ./dockerfiles/telegram/Dockerfile
47 | command: python3 -u telegram/main.py
48 | depends_on:
49 | - database
50 | tty: true
51 | environment:
52 | - PYTHONPATH=.
53 |
54 | networks:
55 | database_network:
56 | driver: bridge
57 |
58 | volumes:
59 | avatars:
60 | external: true
--------------------------------------------------------------------------------
/dockerfiles/nginx/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:latest
2 | COPY ./nginx/nginx.conf /etc/nginx/
3 | COPY ./nginx/static static
4 |
--------------------------------------------------------------------------------
/dockerfiles/postgres/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM postgres:latest
--------------------------------------------------------------------------------
/dockerfiles/telegram/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 |
3 | RUN mkdir /telegram
4 | COPY telegram/requirements.txt /telegram
5 |
6 | RUN python3 -m pip install --no-cache-dir --no-warn-script-location --upgrade pip \
7 | && python3 -m pip install --no-cache-dir --no-warn-script-location --user -r /telegram/requirements.txt
8 |
9 | COPY telegram /telegram
10 |
11 |
--------------------------------------------------------------------------------
/dockerfiles/web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 |
3 | COPY web_django/requirements.txt .
4 |
5 | RUN python3 -m pip install --no-cache-dir --no-warn-script-location --upgrade pip \
6 | && python3 -m pip install --no-cache-dir --no-warn-script-location --user -r requirements.txt
7 |
8 | COPY web_django/ .
9 |
10 |
11 |
--------------------------------------------------------------------------------
/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | user root;
2 | worker_processes auto;
3 | pid /run/nginx.pid;
4 | include /etc/nginx/modules-enabled/*.conf;
5 |
6 |
7 | events {
8 | worker_connections 768;
9 | }
10 |
11 | http {
12 |
13 | include /etc/nginx/mime.types;
14 | sendfile on;
15 |
16 | upstream chess_backend {
17 | server daphne:8000;
18 | }
19 |
20 |
21 | server {
22 | listen 80;
23 | server_name 127.0.0.1;
24 |
25 |
26 | location /static {
27 | alias /static/;
28 | autoindex on;
29 | }
30 |
31 | location / {
32 | proxy_pass http://chess_backend;
33 | proxy_http_version 1.1;
34 | proxy_set_header Upgrade $http_upgrade;
35 | proxy_set_header Connection "Upgrade";
36 | proxy_set_header Host $http_host;
37 | }
38 |
39 | location /favicon.ico {
40 | alias ./web_django/static/favicon.ico;
41 | }
42 |
43 | proxy_set_header Host $http_host;
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/nginx/static/css/authorization_form.css:
--------------------------------------------------------------------------------
1 | body {font-size: 30px; background:#E6E6FA; margin: 0 !important; min-width: 320px; text-align: center; display: grid !important;}
2 | .popup .into {padding: 40px; position: relative;max-width: 613px; border-radius: 20px; flex: 0 1 auto; border: 1px solid rgba(0, 0, 0, 0); background: linear-gradient(138deg, #F4F3F0 0%, #F2F0E9 100%); margin-left: auto; margin-right: auto;}
3 | .popup .into .close {position: absolute; top: 15px; right: 15px; cursor: pointer;}
4 | input {box-sizing: border-box;}
5 | label {font-size: 20px; margin-left: auto}
6 |
7 | @media (max-width: 613px){
8 | .into{
9 | max-width: 100% !important;
10 | }
11 | }
12 |
13 | input {
14 | display: block;
15 | width: 100%;
16 | height: calc(2.25rem + 2px);
17 | padding: 0.375rem 0.75rem;
18 | font-family: inherit;
19 | font-size: 1rem;
20 | font-weight: 400;
21 | line-height: 1.5;
22 | color: #212529;
23 | background-color: #fff;
24 | background-clip: padding-box;
25 | border: 1px solid #bdbdbd;
26 | border-radius: 0.25rem;
27 | transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
28 | }
29 |
30 | input::placeholder {
31 | color: #212529;
32 | opacity: 0.4;
33 | }
34 |
35 | button {
36 | min-width: 100px;
37 | font-family: inherit;
38 | appearance: none;
39 | border: 0;
40 | border-radius: 5px;
41 | background: #656964;
42 | color: #fff;
43 | padding: 8px 16px;
44 | font-size: 1rem;
45 | cursor: pointer;
46 | }
47 |
48 | button:hover {
49 | background: #5c5e5c;
50 | }
51 |
52 | button:focus {
53 | outline: none;
54 | box-shadow: 0 0 0 4px #cbd6ee;
55 | }
56 |
57 | @media (max-height )
--------------------------------------------------------------------------------
/nginx/static/css/base.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap');
2 |
3 | html{
4 | height: 100% !important}
5 |
6 | body{
7 | margin: 0 !important;
8 | display: flex !important;
9 | flex-direction: column !important;
10 | min-height: 100% !important;
11 | height: 100% !important;
12 | font-family: Montserrat;
13 | }
14 |
15 |
16 | footer {
17 | font-family: Montserrat !important;
18 | background: #dedede;
19 | position: relative !important;
20 | margin-top: 50px !important;
21 | }
22 |
23 | .footer-col{
24 | height: 100% !important;
25 | display: inline-block !important;
26 | width: 32%;
27 | }
28 |
29 | @media (max-width: 1000px){
30 | #contacts {
31 | width: 49%;
32 | }
33 |
34 | #help {
35 | width: 49%;
36 | }
37 |
38 | #project_resources{
39 | width: 100%;
40 | }
41 | }
42 |
43 | @media (max-width: 450px){
44 | .footer-container{
45 | display: flex;
46 | flex-direction: column;
47 | justify-content: space-around;
48 | }
49 |
50 | .footer-col{
51 | width: 100% !important;
52 | }
53 | }
54 |
55 | .footer_list_heading{
56 | text-align: center !important;
57 | font-size: 25px !important;}
58 |
59 | .footer_section{
60 | display: flex !important;
61 | position: relative !important;
62 | align-items: center !important;
63 | justify-content: center !important;
64 | }
65 |
66 | .footer_list{padding: 0 !important}
67 |
68 | .footer_item{
69 | text-align: center !important;
70 | font-size: 20px !important;
71 | list-style: none !important;
72 | }
73 |
74 | a{text-decoration: none; color: #b08989}
75 |
76 | a:hover{
77 | color: #7d4343;
78 | }
79 |
80 | .magic_div{
81 | display: none
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/nginx/static/css/chessboards.css:
--------------------------------------------------------------------------------
1 | *{
2 | padding: 0;
3 | box-sizing: border-box;
4 | }
5 |
6 | /* Задаём первоначальные стили элементам */
7 | :root{
8 | /* Задаём стандартные цвета элементам */
9 | --bg-header: #242424;
10 | --bg-button1: #FF1F44;
11 | --bg-button2: #C22A2A;
12 | --bg-main: #121212;
13 | --bg-footer: #242424;
14 | --bg-player-content: rgb(18,66,43);
15 | --gradient-player-content: linear-gradient(90deg, rgba(18,66,43,1) 0%, rgba(36,36,36,1) 100%);
16 | --bg-table-content: rgba(255, 31, 68, 0.2);
17 | --bg-table: #242424;
18 | --border-table: 1px solid #313131;
19 | --color-text-header: #fff;
20 | --color-text-wrap: #A4A4A4;
21 | --color-link-footer: #818181;
22 | --bg-modal-button: #383838;
23 |
24 | /* Задаём стандартные размеры элементов */
25 | --body-width: 1728px;
26 | --navbar-width: 100%;
27 | --navbar-height: 72px;
28 | --navbar-sec-width: 50%;
29 | --navbar-sec-wrap-height: 40px;
30 | --index-header: 3;
31 | --index-main: 2;
32 | --padding-main: 70px 88px;
33 | --padding-header: 0px 88px;
34 | --main-iframe-size: 800px;
35 | --chessboard-bg-size: 200px;
36 | --chessboard-square-size: 100px;
37 | --main-inner_iframe-size: 800px;
38 | --table-size-number: 56px;
39 | --modal-width: 580px;
40 | }
41 |
42 |
43 | /* Выдаём стили главным элементам */
44 | html{
45 | background: var(--bg-main);
46 | }
47 |
48 | body{
49 | max-width: 100%;
50 | min-height: 100%;
51 | margin: 0 auto;
52 | }
53 |
54 | header{
55 | background: var(--bg-header);
56 | z-index: var(--index-header);
57 | transition: 0.3s;
58 | }
59 |
60 | footer{
61 | background: var(--bg-footer);
62 | }
63 |
64 | h1 {
65 | color: var(--color-text-header);
66 | }
67 |
68 | /* Стили для панели навигации () */
69 |
70 | .navbar{
71 | display: flex;
72 | justify-content: center;
73 | width: var(--navbar-width);
74 | min-height: var(--navbar-height);
75 | position: fixed;
76 | left: 0;
77 | top: 0;
78 | margin: 0 auto;
79 | width: 100%;
80 | }
81 |
82 | .navbar .wrap{
83 | display: flex;
84 | flex-direction: row;
85 | max-width: var(--body-width);
86 | min-height: var(--navbar-height);
87 | position: relative;
88 | align-items: center;
89 | justify-content: space-between;
90 | width: 100%;
91 | }
92 |
93 | @media (max-width: 1816px) {
94 | .navbar .wrap, .navbar{
95 | width: 100%;
96 | }
97 | .navbar .wrap{
98 | padding: var(--padding-header);
99 | }
100 | }
101 |
102 | .navbar .navsec{
103 | display: flex;
104 | /* width: calc(var(--body-width) / 2); */
105 | min-height: var(--navbar-height);
106 | align-items: center;
107 | justify-content: flex-start;
108 | transition: 0.3s;
109 | user-select: none;
110 | }
111 |
112 | .navbar .navsec:last-child{
113 | justify-content: flex-end;
114 | }
115 |
116 | .navsec .wrap{
117 | display: flex;
118 | height: var(--navbar-sec-wrap-height);
119 | font-family: 'Inter';
120 | font-weight: 400;
121 | font-size: 16px;
122 | line-height: 24px;
123 | text-align: center;
124 | color: var(--color-text-wrap);
125 | align-items: center;
126 | max-width: 100%;
127 | }
128 |
129 | .navsec .wrap.logo{
130 | justify-content: flex-start;
131 | padding-left: 0;
132 | padding: 0;
133 | }
134 |
135 | .navsec .wrap.logo a{
136 | display: inline-flex;
137 | text-decoration: none;
138 | font-size: 24px;
139 | font-weight: 500;
140 | line-height: 29.05px;
141 | color: var(--color-text-wrap);
142 | }
143 |
144 | .burgermenu{
145 | display: flex;
146 | width: 40px;
147 | height: 40px;
148 | justify-content: center;
149 | align-items: center;
150 | flex-direction: column;
151 | cursor: pointer;
152 | user-select: none;
153 | }
154 |
155 | .burgermenu img{
156 | pointer-events: none;
157 | }
158 |
159 | .navsec nav{
160 | display: flex;
161 | flex-direction: row;
162 | min-height: var(--navbar-sec-wrap-height);
163 | gap: 4px;
164 | }
165 |
166 | .navsec nav .inner, .navsec nav .inner .menu{
167 | display: flex;
168 | flex-direction: row;
169 | align-items: center;
170 | color: var(--color-text-wrap);
171 | font-family: Inter;
172 | font-size: 16px;
173 | font-style: normal;
174 | font-weight: 400;
175 | line-height: 24px; /* 150% */
176 |
177 | }
178 |
179 | .navsec nav .inner .menu{
180 | cursor: pointer;
181 | }
182 |
183 | .navsec nav .inner .menu p {
184 | margin-left: 4px;
185 | margin-right: 4px;
186 | }
187 |
188 | .navsec nav .inner .menu::before{
189 | content: "";
190 | display: block;
191 | width: 24px;
192 | height: 16px;
193 | background-position: center;
194 | background-repeat: no-repeat;
195 | background-size: contain;
196 | }
197 |
198 | .navsec nav .inner .menu[lang="English"]::before{background-image: url(../images/US_menu_icon_flag.png);}
199 |
200 | .menu_icon{
201 | width: 24px;
202 | height: 24px;
203 | }
204 |
205 | .navsec nav .inner .link{
206 | display: inline-flex;
207 | color: var(--color-text-wrap);
208 | text-align: center;
209 | font-family: Inter;
210 | font-size: 16px;
211 | font-style: normal;
212 | font-weight: 400;
213 | line-height: 24px; /* 150% */
214 | text-decoration: none;
215 | margin-left: 24px;
216 | }
217 |
218 | .navsec nav .inner .link:last-child{
219 | margin-right: 24px;
220 | }
221 |
222 | .navsec nav .nav_btn.type1, .navsec nav .nav_btn{
223 | padding: 8px 16px;
224 | justify-content: center;
225 | align-items: center;
226 | gap: 8px;
227 | background: none;
228 | border: 1px solid var(--bg-button1);
229 | color: var(--bg-button1);
230 | cursor: pointer;
231 | margin-right: 16px;
232 | }
233 |
234 | .navsec nav .nav_btn.type1:last-child, .navsec nav .nav_btn:last-child{
235 | margin-right: 0;
236 | }
237 |
238 |
239 | .navsec nav .nav_btn.type2{
240 | background: var(--bg-button1);
241 | color: var(--color-text-header);
242 | }
243 |
244 | /* Стили для тела */
245 |
246 | #prepare_time_div{
247 | height: 100%;
248 | background: #ff9c56ef;
249 | width: 100%;
250 | z-index: 10;
251 | }
252 |
253 | #prepare_title{
254 | font-size: 3vh;
255 | text-align: center;
256 | }
257 |
258 | #prepare_counter{
259 | font-size: 4vh;
260 | text-align: center;
261 | color: black !important;
262 | }
263 |
264 | #prepare_text{
265 | display: flex;
266 | width: 100%;
267 | height: 100%;
268 | flex-direction: column;
269 | justify-content: center;
270 | }
271 |
272 | main{
273 | display: flex;
274 | position: relative;
275 | top: var(--navbar-height);
276 | width: 100%;
277 | }
278 |
279 | main .wrap:first-child{
280 | min-height: calc(100% - var(--navbar-height));
281 | }
282 |
283 | main .wrap{
284 | display: flex;
285 | padding: 70px 88px;
286 | flex-direction: column;
287 | align-items: center;
288 | gap: 64px;
289 | align-self: stretch;
290 | z-index: var(--index-main);
291 | width: 100%;
292 | }
293 |
294 | main .wrap nav {
295 | display: flex;
296 | flex-wrap: wrap;
297 | flex-direction: row;
298 | }
299 |
300 | .nav_btn {
301 | display: flex;
302 | padding: 16px 32px;
303 | justify-content: center;
304 | align-items: center;
305 | gap: 8px;
306 | background: var(--bg-button2);
307 | font-family: 'Inter';
308 | font-size: 16px;
309 | font-style: normal;
310 | font-weight: 500;
311 | line-height: 24px; /* 150% */
312 | text-transform: uppercase;
313 | color: var(--color-text-header);
314 | cursor: pointer;
315 | user-select: none;
316 | }
317 |
318 | .nav_btn::before{
319 | content: "";
320 | width: 24px;
321 | height: 24px;
322 | background-position: center;
323 | background-repeat: no-repeat;
324 | background-size: contain;
325 | display: none;
326 | }
327 |
328 | .nav_btn[icon="Draw"]::before{
329 | background-image: url(../images/Icon\ Leftdraw.png);
330 | display: block;
331 | }
332 |
333 | .nav_btn[icon="Revenge"]::before{
334 | background-image: url(../images/Icon\ LeftRevenge.png);
335 | display: block;
336 | }
337 | .nav_btn[icon="Give up"]::before{
338 | background-image: url(../images/Icon\ LeftGive\ up.png);
339 | display: block;
340 | }
341 |
342 | main .wrap nav .nav_btn {
343 | width: 500px;
344 | margin-right: 16px;
345 | flex: 1 0 0;
346 | }
347 |
348 | main .wrap nav .nav_btn:last-child{
349 | margin-right: 0;
350 | }
351 |
352 | /* Секция для шахматной доски и статистик */
353 |
354 | main .wrap .sec{
355 | display: flex;
356 | align-items: stretch;
357 | gap: 32px;
358 | align-self: stretch;
359 | width: 100%;
360 | min-height: 800px;
361 | }
362 |
363 | main .wrap .sec .inner {
364 | display: flex;
365 | flex-direction: column;
366 | height: 100%;
367 | margin-right: 32px;
368 | /* align-items: center; */
369 | height: 100%;
370 | }
371 |
372 | main .wrap .sec .inner:last-child{
373 | margin-right: 0;
374 | }
375 |
376 | main .wrap .sec .inner .sidebar{
377 | height: 100%;
378 | }
379 |
380 | .table{
381 | display: flex;
382 | flex-direction: column;
383 | height: 50%;
384 | }
385 |
386 | main .wrap .sec .inner.chess_inner{
387 | justify-content: center;
388 | align-items: center;
389 | width: var(--main-inner_iframe-size);
390 | }
391 |
392 | main .wrap .sec .inner.chess_static{
393 | width: calc(100% - var(--main-inner_iframe-size) - 32px);
394 | }
395 |
396 | .square img{
397 | position: absolute;
398 | width: 100%;
399 | height: 100%;
400 | background-size: contain;
401 | background-position: center;
402 | user-select: none;
403 | }
404 |
405 | .square:hover{
406 | background: #66820369;
407 | opacity: 35;
408 | }
409 |
410 | .last_move{
411 | background: #9bc70069;
412 | }
413 |
414 | .squareNotation{
415 | user-select: none;
416 | }
417 |
418 | .inner.chess_inner{
419 | min-width: var(--main-iframe-size);
420 | min-height: var(--main-iframe-size);
421 | overflow: hidden;
422 | }
423 |
424 | table.iksweb{
425 | display: flex;
426 | width: 100%;
427 | height: 100%;
428 | position: relative;
429 | border-collapse:collapse;
430 | border-spacing:0;
431 | height: auto;
432 | z-index: 3;
433 | flex-direction: column;
434 |
435 | }
436 |
437 | tbody.player::before{
438 | content: "";
439 | display: block;
440 | position: absolute;
441 | top: 0;
442 | left: 0;
443 | background: #12B76A;
444 | flex: 1 0 0;
445 | width: 135px;
446 | height: 4px;
447 | z-index: 4;
448 | }
449 |
450 | /* table.iksweb,table.iksweb td, table.iksweb th {
451 | border: 1px solid #595959;
452 | } */
453 | table.iksweb{
454 |
455 | }
456 |
457 | .table.history table tbody{
458 | background: var(--bg-table);
459 | }
460 |
461 |
462 | table.iksweb td,table.iksweb th {
463 | display: flex;
464 | padding: 3px;
465 | width: 30px;
466 | height: 35px;
467 | align-items: center;
468 | font-family: 'Inter';
469 | font-size: 20px;
470 | font-style: normal;
471 | font-weight: 500;
472 | line-height: normal;
473 | text-transform: uppercase;
474 | }
475 | table.iksweb th {
476 | background: #347c99;
477 | color: #fff;
478 | font-weight: normal;
479 | }
480 |
481 |
482 | table.iksweb tbody.player{
483 | display: block;
484 | float: left;
485 | background: var(--gradient-player-content);
486 | height: 128px;
487 | width: 100%;
488 | gap: 8px;
489 | /* padding: 16px !important; */
490 | margin-bottom: 16px;
491 | }
492 |
493 | table.iksweb tbody.player:last-child{
494 | margin-bottom: 32px;
495 | position: relative;
496 | }
497 |
498 | table.iksweb tbody.player tr{
499 | display: flex;
500 | flex-direction: row;
501 | width: 100%;
502 | }
503 |
504 | td{
505 | height: 64px !important;
506 | width: 100%;
507 | padding: 16px !important;
508 | }
509 |
510 | td#player_avatar_wrap{
511 | display: flex;
512 | width: 64px !important;
513 | height: 100%;
514 | justify-content: center;
515 | align-items: center;
516 | }
517 | td.player_time{
518 | font-family: 'Placid Armor' !important;
519 | font-size: 32px !IMPORTANT;
520 | font-style: normal !important;
521 | font-weight: 500 !IMPORTANT;
522 | line-height: 40px !important; /* 125% */
523 | letter-spacing: -0.8px !important;
524 | text-transform !important: uppercase;
525 | width: 100% !IMPORTANT;
526 | }
527 |
528 | #player_avatar{
529 | width: 48px;
530 | height: 48px;
531 | border-radius: 50%;
532 | }
533 |
534 | td#player_name_wrap{
535 | width: calc(100% - 64px);
536 | }
537 |
538 | table.iksweb td.header{
539 | background: #343434;
540 | font-family: Inter;
541 | font-size: 14px;
542 | font-style: normal;
543 | font-weight: 400;
544 | line-height: 24px; /* 171.429% */
545 | color: #FFF;
546 | /* width: calc((100% - var(--table-size-number)) / 2); */
547 | }
548 |
549 | table.iksweb td.number, .table.iksweb td.center{
550 | justify-content: center;
551 | }
552 |
553 | table.iksweb td.number{
554 | width: var(--table-size-number);
555 | }
556 |
557 | table.iksweb td{
558 | display: flex;
559 | align-items: center;
560 | width: calc((100% - var(--table-size-number)) / 2);
561 | text-align: center;
562 | font-family: 'Inter';
563 | font-size: 14px;
564 | font-style: normal;
565 | font-weight: 400;
566 | line-height: 24px; /* 171.429% */
567 | color: var(--color-text-wrap);
568 | }
569 |
570 | table.iksweb td.last{
571 | background: linear-gradient(0deg, rgba(255, 31, 68, 0.20) 0%, rgba(255, 31, 68, 0.20) 100%), var(--bg-header);
572 | color: #FFF;
573 | font-weight: 600;
574 | line-height: 24px; /* 150% */
575 | }
576 |
577 | table.iksweb tbody tr{
578 | display: flex;
579 | /* padding: 16px; */
580 | /* background: var(--bg-table); */
581 | /* border: var(--border-table); */
582 | flex-direction: row;
583 | justify-content: center;
584 | align-items: center;
585 | }
586 |
587 | .table.history table.iksweb tbody tr{
588 | height: calc(100% / 6) !important;
589 |
590 | }
591 |
592 | /* Стили для модального окошечка */
593 | .modal{
594 | display: flex;
595 | position: fixed;
596 | top: 35%;
597 | left: calc(50% - var(--modal-width) / 2);
598 | z-index: 999;
599 | width: var(--modal-width);
600 | padding: 24px;
601 | flex-direction: column;
602 | align-items: center;
603 | gap: 24px;
604 | background: var(--bg-main);
605 | font-family: 'Inter';
606 | font-style: normal;
607 | text-align: center;
608 | visibility: hidden;
609 | }
610 |
611 | .modal h1{
612 | font-size: 20px;
613 | font-weight: 500;
614 | line-height: normal;
615 | text-transform: uppercase;
616 | }
617 |
618 | .modal p{
619 | font-size: 16px;
620 | font-weight: 400;
621 | line-height: 24px; /* 150% */
622 | color: var(--color-text-wrap);
623 | }
624 |
625 | .modal nav{
626 | display: flex;
627 | flex-direction: row;
628 | width: 100%;
629 | align-items: center;
630 | justify-content: space-evenly;
631 | }
632 |
633 | .modal nav .nav_btn {
634 | display: flex;
635 | padding: 8px 16px;
636 | justify-content: center;
637 | align-items: center;
638 | gap: 8px;
639 | flex: 1 0 0;
640 | background: var(--bg-modal-button);
641 | margin-right: 8px;
642 | margin-left: 8px;
643 | color: var(--color-text-wrap);
644 | }
645 |
646 | .modal nav .nav_btn.type2{
647 | background: var(--bg-button1);
648 | color: #FFF;
649 | }
650 |
651 | /* Адаптация под маленькие разрешения экрана */
652 | @media (max-width: 1536px) {
653 | table.iksweb td.header, table.iksweb td{
654 | font-size: 12px;
655 | }
656 | }
657 |
658 | @media (max-width: 800px){
659 | .inner.chess_inner{
660 | width: 100% !important;
661 | height: 100vw !important;
662 | min-height: 100vw !important;
663 | min-width: 100vw !important;
664 | }
665 | }
666 |
667 | @media (max-width: 1400px){
668 |
669 | main .wrap .sec{
670 | flex-direction: column;
671 | align-items: center;
672 | justify-content: c;
673 | }
674 |
675 | main .wrap .sec .inner.chess_static{
676 | width: 100%;
677 | }
678 |
679 | table.iksweb td.header, table.iksweb td{
680 | font-size: 16px;
681 | }
682 |
683 | main .wrap .sec .inner{
684 | margin-right: 0;
685 | }
686 | }
687 |
688 | @media (max-width: 1152px){
689 | .navsec nav .inner .link, .navsec nav .inner, .navsec nav .inner .menu{
690 | font-size: 14px;
691 | }
692 | }
693 |
694 | @media (max-width: 982px){
695 | /* Код адаптации для шахматной доски под маленькие устройства */
696 |
697 | /* main .wrap .sec .inner.chess_inner{
698 | width: calc(var(--main-inner_iframe-size) / 2);
699 | }
700 |
701 | #chess_frame{
702 | min-width: calc(var(--main-iframe-size) /2);
703 | min-height: calc(var(--main-iframe-size) /2);
704 | }
705 |
706 | #chessBoard{
707 | width: calc(var(--main-iframe-size) / 2) !important;
708 | height: calc(var(--main-iframe-size) / 2) !important;
709 | background-size: calc(var(--chessboard-bg-size) / 2) !important;
710 | }
711 |
712 | .square{
713 | width: calc(var(--chessboard-square-size) / 2) !important;
714 | height: calc(var(--chessboard-square-size) / 2) !important;
715 | position: static !important;
716 | float: left;
717 | } */
718 |
719 | main .wrap nav{
720 | flex-direction: column;
721 | width: 100%;
722 | justify-content: center;
723 | align-items: center;
724 | }
725 |
726 |
727 | main .wrap nav .nav_btn{
728 | margin-right: 0;
729 | margin-bottom: 16px;
730 | width: 100% !important;
731 | max-width: 500px;
732 | }
733 |
734 | main .wrap nav .nav_btn:last-child{
735 | margin-bottom: 0;
736 | }
737 | }
738 |
739 | .legal_move{
740 | width: 25%;
741 | height: 25%;
742 | margin-right: auto;
743 | margin-left: auto;
744 | opacity: .7;
745 | border-radius: 50%;
746 | background: green;
747 | position: relative;
748 | top: 50%;
749 | transform: translate(0, -50%);
750 | }
751 |
752 | @media (max-width: 640px){
753 | #player_name{
754 | font-size: 20px;
755 | }
756 |
757 | main .wrap{
758 | padding: 70px 16px;
759 | }
760 |
761 | .modal{
762 | width: 100%;
763 | left: 0;
764 | }
765 |
766 | .modal nav{
767 | flex-direction: column;
768 | }
769 |
770 | .modal nav .nav_btn{
771 | width: 100%;
772 | margin-bottom: 16px;
773 | margin-left: 0;
774 | margin-right: 0;
775 | }
776 | .modal nav .nav_btn:last-child{
777 | margin-bottom: 0;
778 | }
779 |
780 | .navbar .wrap {
781 | padding: 0 20px;
782 | }
783 | .navsec .wrap.logo a {
784 | font-size: 18px;
785 | }
786 | }
787 |
788 | /* board */
789 | #chessBoard {
790 | /* background: url("/static/images/45.gif") !important; */
791 | background-size: 25% !important;;
792 | background-repeat: repeat !important;
793 | overflow: hidden;
794 | border:1px solid #D3A36A;
795 | width: var(--main-iframe-size);
796 | height: var(--main-iframe-size);
797 | }
798 | .squareNotation { padding-left:1px; z-index:1; }
799 | .square.oddX.evenY .squareNotation,
800 | .square.evenX.oddY .squareNotation { color:#D3A36A; }
801 | .square.evenX.evenY .squareNotation,
802 | .square.oddX.oddY .squareNotation { color:#EDC9A2; }
803 |
804 | /* pieces */
805 | .draggablePiece { z-index: 2; cursor:pointer;}
806 | .wp { background: url("/static/images/wp.png") no-repeat; }
807 | .wr { background: url("/static/images/wr.png") no-repeat; }
808 | .wn { background: url("/static/images/wn.png") no-repeat; }
809 | .wb { background: url("/static/images/wb.png") no-repeat; }
810 | .wk { background: url("/static/images/wk.png") no-repeat; }
811 | .wq { background: url("/static/images/wq.png") no-repeat; }
812 |
813 | .bp { background: url("/static/images/bp.png") no-repeat; }
814 | .br { background: url("/static/images/br.png") no-repeat; }
815 | .bn { background: url("/static/images/bn.png") no-repeat; }
816 | .bb { background: url("/static/images/bb.png") no-repeat; }
817 | .bk { background: url("/static/images/bk.png") no-repeat; }
818 | .bq { background: url("/static/images/bq.png") no-repeat; }
819 |
820 | .static_file {display: none;}
--------------------------------------------------------------------------------
/nginx/static/css/error_page.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap');
2 |
3 | body{
4 | background: #FFFFFF;
5 | }
6 |
7 | h1{
8 | font-family: Montserrat;
9 | color: #C0C0C0;
10 | text-align: center
11 | }
12 |
13 | #popup{
14 | display: flex;
15 | flex-direction: column;
16 | height: 100%;
17 | margin: 0 auto;
18 | }
19 |
20 | @media (max-width: 830px){
21 | #popup{
22 | max-width: 100%;
23 | width: 100%;
24 | }
25 |
26 | .error_img{
27 | max-width: 100%;
28 | width: 100%;
29 | }
30 |
31 | h1{font-size: 20px;}
32 | }
33 |
--------------------------------------------------------------------------------
/nginx/static/css/fonts.css:
--------------------------------------------------------------------------------
1 | /* Inter */
2 | @font-face{
3 | font-family: 'Inter';
4 | src: url(../fonts/Inter-VariableFont_slnt\,wght.ttf);
5 | }
6 |
7 | /* Placid Armor */
8 | @font-face{
9 | font-family: "Placid Armor";
10 | src: url(../fonts/PlacidArmor-Medium.ttf);
11 | }
--------------------------------------------------------------------------------
/nginx/static/css/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | header, hgroup, menu, nav, section {
29 | display: block;
30 | }
31 | body {
32 | line-height: 1;
33 | }
34 | ol, ul {
35 | list-style: none;
36 | }
37 | blockquote, q {
38 | quotes: none;
39 | }
40 | blockquote:before, blockquote:after,
41 | q:before, q:after {
42 | content: '';
43 | content: none;
44 | }
45 | table {
46 | border-collapse: collapse;
47 | border-spacing: 0;
48 | }
--------------------------------------------------------------------------------
/nginx/static/css/success_authorization.css:
--------------------------------------------------------------------------------
1 | h1{color: #C0C0C0;
2 | text-align: center}
3 |
4 | .success_img{
5 | display: block;
6 | margin: auto;
7 | }
8 |
9 | .container{
10 | position: relative;
11 | left: 0;
12 | right: 0;
13 | margin: 0 auto;
14 | display: block;
15 | height: 100%;
16 | }
17 |
18 | @media (max-width: 830px){
19 | .container{
20 | max-width: 100%;
21 | width: 100%;
22 | }
23 |
24 | .success_img{
25 | max-width: 100%;
26 | width: 100%;
27 | }
28 |
29 | h1{font-size: 20px;}
30 | }
--------------------------------------------------------------------------------
/nginx/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/favicon.ico
--------------------------------------------------------------------------------
/nginx/static/fonts/Inter-VariableFont_slnt,wght.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/fonts/Inter-VariableFont_slnt,wght.ttf
--------------------------------------------------------------------------------
/nginx/static/fonts/PlacidArmor-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/fonts/PlacidArmor-Medium.ttf
--------------------------------------------------------------------------------
/nginx/static/images/1x1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/1x1.png
--------------------------------------------------------------------------------
/nginx/static/images/45.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/45.gif
--------------------------------------------------------------------------------
/nginx/static/images/45_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/45_1.gif
--------------------------------------------------------------------------------
/nginx/static/images/45_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/45_3.gif
--------------------------------------------------------------------------------
/nginx/static/images/45_reversed.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/45_reversed.gif
--------------------------------------------------------------------------------
/nginx/static/images/Avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/Avatar.png
--------------------------------------------------------------------------------
/nginx/static/images/Icon LeftGive up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/Icon LeftGive up.png
--------------------------------------------------------------------------------
/nginx/static/images/Icon LeftRevenge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/Icon LeftRevenge.png
--------------------------------------------------------------------------------
/nginx/static/images/Icon Leftdraw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/Icon Leftdraw.png
--------------------------------------------------------------------------------
/nginx/static/images/US_menu_icon_flag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/US_menu_icon_flag.png
--------------------------------------------------------------------------------
/nginx/static/images/bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/bb.png
--------------------------------------------------------------------------------
/nginx/static/images/bk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/bk.png
--------------------------------------------------------------------------------
/nginx/static/images/bn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/bn.png
--------------------------------------------------------------------------------
/nginx/static/images/bp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/bp.png
--------------------------------------------------------------------------------
/nginx/static/images/bq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/bq.png
--------------------------------------------------------------------------------
/nginx/static/images/br.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/br.png
--------------------------------------------------------------------------------
/nginx/static/images/burgermenu_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/burgermenu_icon.png
--------------------------------------------------------------------------------
/nginx/static/images/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/error.png
--------------------------------------------------------------------------------
/nginx/static/images/success_authorization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/success_authorization.png
--------------------------------------------------------------------------------
/nginx/static/images/unknown_user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/unknown_user.png
--------------------------------------------------------------------------------
/nginx/static/images/wb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/wb.png
--------------------------------------------------------------------------------
/nginx/static/images/wk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/wk.png
--------------------------------------------------------------------------------
/nginx/static/images/wn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/wn.png
--------------------------------------------------------------------------------
/nginx/static/images/wp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/wp.png
--------------------------------------------------------------------------------
/nginx/static/images/wq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/wq.png
--------------------------------------------------------------------------------
/nginx/static/images/wr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/nginx/static/images/wr.png
--------------------------------------------------------------------------------
/nginx/static/js/authorization_response.js:
--------------------------------------------------------------------------------
1 | function check_token(){
2 |
3 | var token = $("#token").val();
4 |
5 | if (token.length < 35){
6 |
7 | $("label").replaceWith("");
8 | return false;
9 |
10 | }
11 | console.log(config)
12 | $.ajax(
13 | {
14 | type: 'POST',
15 | url: `${config.ssl_http}://${config.domain}/api/v1/authorize?token=${token}`,
16 | success: function(data){
17 | var response = $.parseJSON(data);
18 |
19 | if (!response.success){
20 | $("label").replaceWith((""));
21 | return false;
22 | }
23 | location.reload(true);
24 | }
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/nginx/static/js/chessboard.js:
--------------------------------------------------------------------------------
1 | function closeModalWindow(modal){
2 | modal.style = {'visibility': 'hidden'};
3 | }
4 |
5 | function showGiveUpModal(){
6 | $('#give_up_modal').css({'visibility': 'visible'});
7 | }
8 |
9 | function showDrawOfferModal(){
10 | socket_connection.send(JSON.stringify({"type": "draw_offer"}));
11 | $('#draw_offer_create_modal').css({"visibility": 'visible'});
12 | draw_offer = true;
13 | }
14 |
15 | function giveUp(){
16 | socket_connection.send(JSON.stringify({"type": "give_up"}));
17 | $('#give_up_modal').css({"visibility": "hidden"});
18 | }
19 |
20 | function drawOfferAccept(){
21 | socket_connection.send(JSON.stringify({"type": "draw_offer"}));
22 | $('#draw_offer_modal').css({"visibility": "hidden"});
23 | }
24 |
25 | function transitionAcceptModal(url){
26 | replaceText(document.getElementById('transition_message'), 'Ты уверен что хочешь перейти на страницу ' + url + '?');
27 | $('#transition_modal').css({'visibility': 'visible'});
28 | }
29 |
30 | function transitionUser(url){
31 | window.location.href = url;
32 | }
33 |
34 | function onMoveSound(){
35 |
36 | var on_move_sound = new Audio($("#on_move_sound").textContent);
37 | on_move_sound.play();
38 | }
39 |
40 | function onReady(prepare_time, user_id, board_fen, first_player_time, second_player_time, draw_offer, color, turn){
41 |
42 | updatePrepareTime(prepare_time);
43 | updateUserId(user_id);
44 | updateBoardFEN(board_fen);
45 | updateFirstPlayerTime(first_player_time);
46 | updateSecondPlayerTime(second_player_time);
47 | updateDrawOffer(draw_offer);
48 | updateColor(color);
49 | updateTurn(turn);
50 |
51 | if (window.prepare_time <= 0){
52 | window.startTimers();
53 | }
54 | }
55 |
56 | function updatePrepareTime(prepare_time_var){
57 | var prepare_time = prepare_time_var;
58 | window.prepare_time = prepare_time;
59 |
60 | window.replaceText($("#prepare_counter")[0], prepare_time);
61 | }
62 |
63 | function updateUserId(user_id_var){
64 | var user_id = user_id_var;
65 | window.user_id = user_id;
66 | }
67 |
68 | function updateBoardFEN(board_fen_var){
69 | var board_fen = board_fen_var;
70 | window.board_fen = board_fen;
71 | }
72 |
73 | function updateDrawOffer(draw_offer_var){
74 | var draw_offer = (draw_offer_var.toLowerCase() == "true");
75 | window.draw_offer = draw_offer;
76 | }
77 |
78 | function updateColor(color_var){
79 |
80 | var color = (color_var.toLowerCase() == "true");
81 | var color_word;
82 |
83 | if (color) color_word = "w";
84 | else color_word = "b";
85 |
86 | window.color = color;
87 | window.color_word = color_word
88 | }
89 |
90 | function updateTurn(turn_var){
91 | var turn = (turn_var.toLowerCase() == "true");
92 | window.turn = turn;
93 | }
94 |
95 | function possibles_to_move(piece_type){
96 | if (check_turn() && check_color(piece_type)) return true;
97 | }
98 |
99 | function check_turn(){
100 | if (window.turn == window.color) return true;
101 | }
102 |
103 | function check_color(piece_type){
104 | if (piece_type[0] == window.color_word) return true;
105 | }
106 |
107 | function showLegalMoves(legal_moves_array){
108 | legal_moves_array.forEach(function(element){
109 | $('#' + element)[0].offsetParent.append($('')[0]);
110 | })
111 | }
112 |
113 | function hideLegalMoves(){
114 | let legal_moves = [...document.getElementsByClassName("legal_move")];
115 | for (i=0; i < legal_moves.length; i++) {
116 | legal_moves[i].remove();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/nginx/static/js/chessboard_spectator.js:
--------------------------------------------------------------------------------
1 | function closeModalWindow(modal){
2 | modal.style = {'visibility': 'hidden'};
3 | }
4 |
5 | function transitionAcceptModal(url){
6 | replaceText(document.getElementById('transition_message'), 'Ты уверен что хочешь перейти на страницу ' + url + '?');
7 | $('#transition_modal').css({'visibility': 'visible'});
8 | }
9 |
10 | function transitionUser(url){
11 | window.location.href = url;
12 | }
13 |
14 | function onReady(prepare_time, board_fen, first_player_time, second_player_time, turn){
15 |
16 | updatePrepareTime(prepare_time);
17 | updateBoardFEN(board_fen);
18 | updateFirstPlayerTime(first_player_time);
19 | updateSecondPlayerTime(second_player_time);
20 | updateTurn(turn);
21 |
22 | if (window.prepare_time <= 0){
23 | window.startTimers();
24 | }
25 | }
26 |
27 | function updatePrepareTime(prepare_time_var){
28 | var prepare_time = prepare_time_var;
29 | window.prepare_time = prepare_time;
30 |
31 | window.replaceText($("#prepare_counter")[0], prepare_time);
32 | }
33 |
34 | function updateBoardFEN(board_fen_var){
35 | var board_fen = board_fen_var;
36 | window.board_fen = board_fen;
37 | }
38 |
39 | function updateTurn(turn_var){
40 | var turn = (turn_var.toLowerCase() == "true");
41 | window.turn = turn;
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/nginx/static/js/config.js:
--------------------------------------------------------------------------------
1 | var config = {
2 | domain: "127.0.0.1",
3 | ssl_http: "http",
4 | ssl_ws: "ws"
5 | }
--------------------------------------------------------------------------------
/nginx/static/js/core.js:
--------------------------------------------------------------------------------
1 | function replaceText(element, text){
2 |
3 | if (element.innerText) {
4 | element.innerText = text;
5 | }
6 |
7 | else if (element.textContent) {
8 | element.textContent = text;
9 | }
10 | }
--------------------------------------------------------------------------------
/nginx/static/js/jquery.chess.js:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License
3 | ===========
4 |
5 | Copyright (c) 2012 Serban Ghita
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a
8 | copy of this software and associated documentation files (the "Software"),
9 | to deal in the Software without restriction, including without limitation
10 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 | and/or sell copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 |
27 | (function($){
28 |
29 | function Chess(){
30 |
31 | var self = this,
32 | $piece,
33 | $pieces,
34 | $board,
35 | $boardInner,
36 | $settings;
37 |
38 | this.init = function(options, elem){
39 |
40 | $settings = $.extend({
41 | squareSize: 12.5,
42 | x : [1,2,3,4,5,6,7,8],
43 | y : [1,2,3,4,5,6,7,8],
44 | xLiteral : ['a','b','c','d','e','f','g','h'],
45 | appendTo: document.body,
46 | type: 'matrix',
47 | position: null
48 | }, options);
49 |
50 | if (window.color === false){
51 | $settings.y = $settings.y.reverse();
52 | let elem = document.getElementById("chessBoard");
53 | elem.style.background = 'url("/static/images/45_reversed.gif")';
54 | }
55 |
56 | else {
57 | let elem = document.getElementById("chessBoard");
58 | elem.style.background = 'url("/static/images/45.gif")';
59 | }
60 |
61 | var path_to_1x1 = $("#static_img").textContent;
62 |
63 | $piece = $('
').attr({src: path_to_1x1, width: $settings.squareSize, height: $settings.squareSize}).addClass('draggablePiece');
64 |
65 | $pieces = {
66 | wp: $piece.clone().addClass('wp'),
67 | wr: $piece.clone().addClass('wr'),
68 | wn: $piece.clone().addClass('wn'),
69 | wb: $piece.clone().addClass('wb'),
70 | wk: $piece.clone().addClass('wk'),
71 | wq: $piece.clone().addClass('wq'),
72 |
73 | bp: $piece.clone().addClass('bp'),
74 | br: $piece.clone().addClass('br'),
75 | bn: $piece.clone().addClass('bn'),
76 | bb: $piece.clone().addClass('bb'),
77 | bk: $piece.clone().addClass('bk'),
78 | bq: $piece.clone().addClass('bq')
79 | };
80 |
81 | $board = $('.inner.chess_inner')
82 | .css({
83 | position: 'relative',
84 | width: $settings.squareSize*8,
85 | height: $settings.squareSize*8
86 | });
87 |
88 | $boardInner = $('').attr({id:'chessBoardInner'}).addClass('chessBoardInner');
89 |
90 | this.buildBoard();
91 | this.buildPosition(false, $settings.type, $settings.position);
92 | this.drawBoard();
93 |
94 | }
95 |
96 | this.buildBoard = function(){
97 |
98 | // @todo - Reverse pieces here.
99 | $settings.newY = $settings.y.slice();
100 | $settings.newY.reverse();
101 |
102 | for(x in $settings.x){
103 | for(y in $settings.newY){
104 |
105 | var tmpSquare = $('')
106 | .attr({id: 'pos'+$settings.x[x]+''+$settings.y[y]})
107 | .css({
108 | left: (($settings.x[x]-1)*$settings.squareSize) + '%',
109 | top: (($settings.newY[y]-1)*$settings.squareSize) + '%',
110 | position: 'absolute',
111 | width: $settings.squareSize + '%',
112 | height: $settings.squareSize + '%'
113 | })
114 | .addClass('square '+(x%2 ? 'evenX' : 'oddX')+' '+(y%2 ? 'evenY' : 'oddY'));
115 | var tmpSquareNotation = $(``)
116 | .css({
117 | position: 'absolute',
118 | bottom:0,
119 | left:0,
120 | fontSize:'56%'
121 | })
122 | .addClass('squareNotation')
123 | .html($settings.xLiteral[$settings.x[x]-1]+''+(parseInt(y)+1));
124 | tmpSquare.append(tmpSquareNotation);
125 | $boardInner.append(tmpSquare);
126 |
127 | }
128 | }
129 |
130 | }
131 |
132 | // Build position, including the start position.
133 |
134 | this.buildPosition = function(start, type, position){
135 |
136 | if(start){
137 |
138 | // Initial black pieces position.
139 | $boardInner.find('#pos17,#pos27,#pos37,#pos47,#pos57,#pos67,#pos77,#pos87').append($pieces.bp);
140 | $boardInner.find('#pos18,#pos88').append($pieces.br);
141 | $boardInner.find('#pos28,#pos78').append($pieces.bn);
142 | $boardInner.find('#pos38,#pos68').append($pieces.bb);
143 | $boardInner.find('#pos58').append($pieces.bk);
144 | $boardInner.find('#pos48').append($pieces.bq);
145 |
146 | // Initial white pieces position.
147 | $boardInner.find('#pos12,#pos22,#pos32,#pos42,#pos52,#pos62,#pos72,#pos82').append($pieces.wp);
148 | $boardInner.find('#pos11,#pos81').append($pieces.wr);
149 | $boardInner.find('#pos21,#pos71').append($pieces.wn);
150 | $boardInner.find('#pos31,#pos61').append($pieces.wb);
151 | $boardInner.find('#pos51').append($pieces.wk);
152 | $boardInner.find('#pos41').append($pieces.wq);
153 |
154 | }
155 |
156 | if(typeof type === 'string'){
157 | switch(type){
158 | case 'matrix':
159 | this._arrangeMatrixPosition(position);
160 | break;
161 | case 'fen':
162 | this._arrangeMatrixPosition(this._fenToPosition(position));
163 | break;
164 | default:
165 | alert(1);
166 | }
167 | }
168 |
169 | // Make pieces draggable.
170 | this._createDraggable($boardInner.find('.draggablePiece'));
171 |
172 | // Make pieces droppable.
173 | this._createDroppable($boardInner.find('.square'));
174 |
175 | }
176 |
177 | this._fenToPosition = function(fen){
178 | var position = [];
179 | var rows = fen.split('/');
180 | for(var i=0; i
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a
8 | copy of this software and associated documentation files (the "Software"),
9 | to deal in the Software without restriction, including without limitation
10 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 | and/or sell copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 |
27 | (function($){
28 |
29 | function Chess(){
30 |
31 | var self = this,
32 | $piece,
33 | $pieces,
34 | $board,
35 | $boardInner,
36 | $settings;
37 |
38 | this.init = function(options, elem){
39 |
40 | $settings = $.extend({
41 | squareSize: 12.5,
42 | x : [1,2,3,4,5,6,7,8],
43 | y : [1,2,3,4,5,6,7,8],
44 | xLiteral : ['a','b','c','d','e','f','g','h'],
45 | appendTo: document.body,
46 | type: 'matrix',
47 | position: null
48 | }, options);
49 |
50 | var path_to_1x1 = $("#static_img").textContent;
51 |
52 | $piece = $('
').attr({src: path_to_1x1, width: $settings.squareSize, height: $settings.squareSize}).addClass('draggablePiece');
53 |
54 | $pieces = {
55 | wp: $piece.clone().addClass('wp'),
56 | wr: $piece.clone().addClass('wr'),
57 | wn: $piece.clone().addClass('wn'),
58 | wb: $piece.clone().addClass('wb'),
59 | wk: $piece.clone().addClass('wk'),
60 | wq: $piece.clone().addClass('wq'),
61 |
62 | bp: $piece.clone().addClass('bp'),
63 | br: $piece.clone().addClass('br'),
64 | bn: $piece.clone().addClass('bn'),
65 | bb: $piece.clone().addClass('bb'),
66 | bk: $piece.clone().addClass('bk'),
67 | bq: $piece.clone().addClass('bq')
68 | };
69 |
70 | $board = $('.inner.chess_inner')
71 | .css({
72 | position: 'relative',
73 | width: $settings.squareSize*8,
74 | height: $settings.squareSize*8
75 | });
76 |
77 | $boardInner = $('').attr({id:'chessBoardInner'}).addClass('chessBoardInner');
78 |
79 | this.buildBoard();
80 | this.buildPosition(false, $settings.type, $settings.position);
81 | this.drawBoard();
82 |
83 | }
84 |
85 | this.buildBoard = function(){
86 |
87 | // @todo - Reverse pieces here.
88 | $settings.newY = $settings.y.slice();
89 | $settings.newY.reverse();
90 |
91 | for(x in $settings.x){
92 | for(y in $settings.newY){
93 |
94 | var tmpSquare = $('')
95 | .attr({id: 'pos'+$settings.x[x]+''+$settings.y[y]})
96 | .css({
97 | left: (($settings.x[x]-1)*$settings.squareSize) + '%',
98 | top: (($settings.newY[y]-1)*$settings.squareSize) + '%',
99 | position: 'absolute',
100 | width: $settings.squareSize + '%',
101 | height: $settings.squareSize + '%'
102 | })
103 | .addClass('square '+(x%2 ? 'evenX' : 'oddX')+' '+(y%2 ? 'evenY' : 'oddY'));
104 | var tmpSquareNotation = $('')
105 | .css({
106 | position: 'absolute',
107 | bottom:0,
108 | left:0,
109 | fontSize:'56%'
110 | })
111 | .addClass('squareNotation')
112 | .html($settings.xLiteral[$settings.x[x]-1]+''+$settings.y[y]);
113 | tmpSquareNotation.id = 'test'
114 | tmpSquare.append(tmpSquareNotation);
115 | $boardInner.append(tmpSquare);
116 |
117 | }
118 | }
119 |
120 | }
121 |
122 | // Build position, including the start position.
123 |
124 | this.buildPosition = function(start, type, position){
125 |
126 | if(start){
127 |
128 | // Initial black pieces position.
129 | $boardInner.find('#pos17,#pos27,#pos37,#pos47,#pos57,#pos67,#pos77,#pos87').append($pieces.bp);
130 | $boardInner.find('#pos18,#pos88').append($pieces.br);
131 | $boardInner.find('#pos28,#pos78').append($pieces.bn);
132 | $boardInner.find('#pos38,#pos68').append($pieces.bb);
133 | $boardInner.find('#pos58').append($pieces.bk);
134 | $boardInner.find('#pos48').append($pieces.bq);
135 |
136 | // Initial white pieces position.
137 | $boardInner.find('#pos12,#pos22,#pos32,#pos42,#pos52,#pos62,#pos72,#pos82').append($pieces.wp);
138 | $boardInner.find('#pos11,#pos81').append($pieces.wr);
139 | $boardInner.find('#pos21,#pos71').append($pieces.wn);
140 | $boardInner.find('#pos31,#pos61').append($pieces.wb);
141 | $boardInner.find('#pos51').append($pieces.wk);
142 | $boardInner.find('#pos41').append($pieces.wq);
143 |
144 | }
145 |
146 | if(typeof type === 'string'){
147 | switch(type){
148 | case 'matrix':
149 | this._arrangeMatrixPosition(position);
150 | break;
151 | case 'fen':
152 | this._arrangeMatrixPosition(this._fenToPosition(position));
153 | break;
154 | default:
155 | alert(1);
156 | }
157 | }
158 | }
159 |
160 | this._fenToPosition = function(fen){
161 | var position = [];
162 | var rows = fen.split('/');
163 | for(var i=0; i
110 | value = parseInt( elem.css( "zIndex" ), 10 );
111 | if ( !isNaN( value ) && value !== 0 ) {
112 | return value;
113 | }
114 | }
115 | elem = elem.parent();
116 | }
117 | }
118 |
119 | return 0;
120 | },
121 |
122 | disableSelection: function() {
123 | return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
124 | ".ui-disableSelection", function( event ) {
125 | event.preventDefault();
126 | });
127 | },
128 |
129 | enableSelection: function() {
130 | return this.unbind( ".ui-disableSelection" );
131 | }
132 | });
133 |
134 | $.each( [ "Width", "Height" ], function( i, name ) {
135 | var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
136 | type = name.toLowerCase(),
137 | orig = {
138 | innerWidth: $.fn.innerWidth,
139 | innerHeight: $.fn.innerHeight,
140 | outerWidth: $.fn.outerWidth,
141 | outerHeight: $.fn.outerHeight
142 | };
143 |
144 | function reduce( elem, size, border, margin ) {
145 | $.each( side, function() {
146 | size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
147 | if ( border ) {
148 | size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
149 | }
150 | if ( margin ) {
151 | size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
152 | }
153 | });
154 | return size;
155 | }
156 |
157 | $.fn[ "inner" + name ] = function( size ) {
158 | if ( size === undefined ) {
159 | return orig[ "inner" + name ].call( this );
160 | }
161 |
162 | return this.each(function() {
163 | $( this ).css( type, reduce( this, size ) + "px" );
164 | });
165 | };
166 |
167 | $.fn[ "outer" + name] = function( size, margin ) {
168 | if ( typeof size !== "number" ) {
169 | return orig[ "outer" + name ].call( this, size );
170 | }
171 |
172 | return this.each(function() {
173 | $( this).css( type, reduce( this, size, true, margin ) + "px" );
174 | });
175 | };
176 | });
177 |
178 | // selectors
179 | function focusable( element, isTabIndexNotNaN ) {
180 | var nodeName = element.nodeName.toLowerCase();
181 | if ( "area" === nodeName ) {
182 | var map = element.parentNode,
183 | mapName = map.name,
184 | img;
185 | if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
186 | return false;
187 | }
188 | img = $( "img[usemap=#" + mapName + "]" )[0];
189 | return !!img && visible( img );
190 | }
191 | return ( /input|select|textarea|button|object/.test( nodeName )
192 | ? !element.disabled
193 | : "a" == nodeName
194 | ? element.href || isTabIndexNotNaN
195 | : isTabIndexNotNaN)
196 | // the element and all of its ancestors must be visible
197 | && visible( element );
198 | }
199 |
200 | function visible( element ) {
201 | return !$( element ).parents().andSelf().filter(function() {
202 | return $.curCSS( this, "visibility" ) === "hidden" ||
203 | $.expr.filters.hidden( this );
204 | }).length;
205 | }
206 |
207 | $.extend( $.expr[ ":" ], {
208 | data: function( elem, i, match ) {
209 | return !!$.data( elem, match[ 3 ] );
210 | },
211 |
212 | focusable: function( element ) {
213 | return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
214 | },
215 |
216 | tabbable: function( element ) {
217 | var tabIndex = $.attr( element, "tabindex" ),
218 | isTabIndexNaN = isNaN( tabIndex );
219 | return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
220 | }
221 | });
222 |
223 | // support
224 | $(function() {
225 | var body = document.body,
226 | div = body.appendChild( div = document.createElement( "div" ) );
227 |
228 | // access offsetHeight before setting the style to prevent a layout bug
229 | // in IE 9 which causes the elemnt to continue to take up space even
230 | // after it is removed from the DOM (#8026)
231 | div.offsetHeight;
232 |
233 | $.extend( div.style, {
234 | minHeight: "100px",
235 | height: "auto",
236 | padding: 0,
237 | borderWidth: 0
238 | });
239 |
240 | $.support.minHeight = div.offsetHeight === 100;
241 | $.support.selectstart = "onselectstart" in div;
242 |
243 | // set display to none to avoid a layout bug in IE
244 | // http://dev.jquery.com/ticket/4014
245 | body.removeChild( div ).style.display = "none";
246 | });
247 |
248 |
249 |
250 |
251 |
252 | // deprecated
253 | $.extend( $.ui, {
254 | // $.ui.plugin is deprecated. Use the proxy pattern instead.
255 | plugin: {
256 | add: function( module, option, set ) {
257 | var proto = $.ui[ module ].prototype;
258 | for ( var i in set ) {
259 | proto.plugins[ i ] = proto.plugins[ i ] || [];
260 | proto.plugins[ i ].push( [ option, set[ i ] ] );
261 | }
262 | },
263 | call: function( instance, name, args ) {
264 | var set = instance.plugins[ name ];
265 | if ( !set || !instance.element[ 0 ].parentNode ) {
266 | return;
267 | }
268 |
269 | for ( var i = 0; i < set.length; i++ ) {
270 | if ( instance.options[ set[ i ][ 0 ] ] ) {
271 | set[ i ][ 1 ].apply( instance.element, args );
272 | }
273 | }
274 | }
275 | },
276 |
277 | // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
278 | contains: function( a, b ) {
279 | return document.compareDocumentPosition ?
280 | a.compareDocumentPosition( b ) & 16 :
281 | a !== b && a.contains( b );
282 | },
283 |
284 | // only used by resizable
285 | hasScroll: function( el, a ) {
286 |
287 | //If overflow is hidden, the element might have extra content, but the user wants to hide it
288 | if ( $( el ).css( "overflow" ) === "hidden") {
289 | return false;
290 | }
291 |
292 | var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
293 | has = false;
294 |
295 | if ( el[ scroll ] > 0 ) {
296 | return true;
297 | }
298 |
299 | // TODO: determine which cases actually cause this to happen
300 | // if the element doesn't have the scroll set, see if it's possible to
301 | // set the scroll
302 | el[ scroll ] = 1;
303 | has = ( el[ scroll ] > 0 );
304 | el[ scroll ] = 0;
305 | return has;
306 | },
307 |
308 | // these are odd functions, fix the API or move into individual plugins
309 | isOverAxis: function( x, reference, size ) {
310 | //Determines when x coordinate is over "b" element axis
311 | return ( x > reference ) && ( x < ( reference + size ) );
312 | },
313 | isOver: function( y, x, top, left, height, width ) {
314 | //Determines when x, y coordinates is over "b" element
315 | return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
316 | }
317 | });
318 |
319 | })( jQuery );
320 |
--------------------------------------------------------------------------------
/nginx/static/js/jquery.ui.droppable.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery UI Droppable @VERSION
3 | *
4 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5 | * Dual licensed under the MIT or GPL Version 2 licenses.
6 | * http://jquery.org/license
7 | *
8 | * http://docs.jquery.com/UI/Droppables
9 | *
10 | * Depends:
11 | * jquery.ui.core.js
12 | * jquery.ui.widget.js
13 | * jquery.ui.mouse.js
14 | * jquery.ui.draggable.js
15 | */
16 |
17 | (function( $, undefined ) {
18 |
19 | $.widget("ui.droppable", {
20 | widgetEventPrefix: "drop",
21 | options: {
22 | accept: '*',
23 | activeClass: false,
24 | addClasses: true,
25 | greedy: false,
26 | hoverClass: false,
27 | scope: 'default',
28 | tolerance: 'intersect'
29 | },
30 | _create: function() {
31 |
32 | var o = this.options, accept = o.accept;
33 | this.isover = 0; this.isout = 1;
34 |
35 | this.accept = $.isFunction(accept) ? accept : function(d) {
36 | return d.is(accept);
37 | };
38 |
39 | //Store the droppable's proportions
40 | this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
41 |
42 | // Add the reference and positions to the manager
43 | $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
44 | $.ui.ddmanager.droppables[o.scope].push(this);
45 |
46 | (o.addClasses && this.element.addClass("ui-droppable"));
47 |
48 | },
49 |
50 | destroy: function() {
51 | var drop = $.ui.ddmanager.droppables[this.options.scope];
52 | for ( var i = 0; i < drop.length; i++ )
53 | if ( drop[i] == this )
54 | drop.splice(i, 1);
55 |
56 | this.element
57 | .removeClass("ui-droppable ui-droppable-disabled")
58 | .removeData("droppable")
59 | .unbind(".droppable");
60 |
61 | return this;
62 | },
63 |
64 | _setOption: function(key, value) {
65 |
66 | if(key == 'accept') {
67 | this.accept = $.isFunction(value) ? value : function(d) {
68 | return d.is(value);
69 | };
70 | }
71 | $.Widget.prototype._setOption.apply(this, arguments);
72 | },
73 |
74 | _activate: function(event) {
75 |
76 | var draggable = $.ui.ddmanager.current;
77 | if(this.options.activeClass) this.element.addClass(this.options.activeClass);
78 | (draggable && this._trigger('activate', event, this.ui(draggable)));
79 | },
80 |
81 | _deactivate: function(event) {
82 | var draggable = $.ui.ddmanager.current;
83 | if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
84 | (draggable && this._trigger('deactivate', event, this.ui(draggable)));
85 | },
86 |
87 | _over: function(event) {
88 |
89 | var draggable = $.ui.ddmanager.current;
90 | if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
91 |
92 | if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
93 | if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
94 | this._trigger('over', event, this.ui(draggable));
95 | }
96 |
97 | },
98 |
99 | _out: function(event) {
100 |
101 | var draggable = $.ui.ddmanager.current;
102 | if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
103 |
104 | if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
105 | if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
106 | this._trigger('out', event, this.ui(draggable));
107 | }
108 |
109 | },
110 |
111 | _drop: function(event,custom) {
112 |
113 | var draggable = custom || $.ui.ddmanager.current;
114 | if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) {return false;} // Bail if draggable and droppable are same element
115 |
116 | var end_cell = this.element.find(":data(droppable)").context.getElementsByClassName('squareNotation')[0].innerText;
117 |
118 | var childrenIntersection = false;
119 | this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
120 | var inst = $.data(this, 'droppable');
121 | if(
122 | inst.options.greedy
123 | && !inst.options.disabled
124 | && inst.options.scope == draggable.options.scope
125 | && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
126 | && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
127 | ) { childrenIntersection = true; return false; }
128 | });
129 | if(childrenIntersection) return false;
130 |
131 | if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
132 | if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
133 | if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
134 | this._trigger('drop', event, this.ui(draggable));
135 | return this.element;
136 | }
137 |
138 |
139 |
140 | return false;
141 |
142 | },
143 |
144 | ui: function(c) {
145 | return {
146 | draggable: (c.currentItem || c.element),
147 | helper: c.helper,
148 | position: c.position,
149 | offset: c.positionAbs
150 | };
151 | }
152 |
153 | });
154 |
155 | $.extend($.ui.droppable, {
156 | version: "@VERSION"
157 | });
158 |
159 | $.ui.intersect = function(draggable, droppable, toleranceMode) {
160 |
161 | if (!droppable.offset) return false;
162 |
163 | var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
164 | y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
165 | var l = droppable.offset.left, r = l + droppable.proportions.width,
166 | t = droppable.offset.top, b = t + droppable.proportions.height;
167 |
168 | switch (toleranceMode) {
169 | case 'fit':
170 | return (l <= x1 && x2 <= r
171 | && t <= y1 && y2 <= b);
172 | break;
173 | case 'intersect':
174 | return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
175 | && x2 - (draggable.helperProportions.width / 2) < r // Left Half
176 | && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
177 | && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
178 | break;
179 | case 'pointer':
180 | var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
181 | draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
182 | isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
183 | return isOver;
184 | break;
185 | case 'touch':
186 | return (
187 | (y1 >= t && y1 <= b) || // Top edge touching
188 | (y2 >= t && y2 <= b) || // Bottom edge touching
189 | (y1 < t && y2 > b) // Surrounded vertically
190 | ) && (
191 | (x1 >= l && x1 <= r) || // Left edge touching
192 | (x2 >= l && x2 <= r) || // Right edge touching
193 | (x1 < l && x2 > r) // Surrounded horizontally
194 | );
195 | break;
196 | default:
197 | return false;
198 | break;
199 | }
200 |
201 | };
202 |
203 | /*
204 | This manager tracks offsets of draggables and droppables
205 | */
206 | $.ui.ddmanager = {
207 | current: null,
208 | droppables: { 'default': [] },
209 | prepareOffsets: function(t, event) {
210 |
211 | var m = $.ui.ddmanager.droppables[t.options.scope] || [];
212 | var type = event ? event.type : null; // workaround for #2317
213 | var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
214 |
215 | droppablesLoop: for (var i = 0; i < m.length; i++) {
216 |
217 | if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
218 | for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
219 | m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
220 |
221 | if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
222 |
223 | m[i].offset = m[i].element.offset();
224 | m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
225 |
226 | }
227 |
228 | },
229 | drop: function(draggable, event) {
230 |
231 | hideLegalMoves();
232 |
233 | var dropped = false;
234 | $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
235 |
236 | if(!this.options) return;
237 | if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
238 | dropped = this._drop.call(this, event) || dropped;
239 |
240 |
241 | if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
242 | this.isout = 1; this.isover = 0;
243 | this._deactivate.call(this, event);
244 | }
245 |
246 | });
247 | if (dropped){
248 | let end_cell = dropped[0];
249 | if (window.start_cell == end_cell){return false;}
250 | move(window.start_cell, end_cell);
251 | }
252 |
253 | return dropped;
254 |
255 | },
256 | dragStart: function( draggable, event ) {
257 |
258 | window.start_cell = draggable.element.context.offsetParent;
259 | getLegalMoves(window.start_cell.getElementsByClassName("squareNotation")[0].innerText);
260 | //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
261 | draggable.element.parents( ":not(body,html)" ).bind( "scroll.droppable", function() {
262 | if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
263 | });
264 | },
265 | drag: function(draggable, event) {
266 |
267 | //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
268 | if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
269 |
270 | //Run through all droppables and check their positions based on specific tolerance options
271 | $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
272 |
273 | if(this.options.disabled || this.greedyChild || !this.visible) return;
274 | var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
275 |
276 | var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
277 | if(!c) return;
278 |
279 | var parentInstance;
280 | if (this.options.greedy) {
281 | var parent = this.element.parents(':data(droppable):eq(0)');
282 | if (parent.length) {
283 | parentInstance = $.data(parent[0], 'droppable');
284 | parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
285 | }
286 | }
287 |
288 | // we just moved into a greedy child
289 | if (parentInstance && c == 'isover') {
290 | parentInstance['isover'] = 0;
291 | parentInstance['isout'] = 1;
292 | parentInstance._out.call(parentInstance, event);
293 | }
294 |
295 | this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
296 | this[c == "isover" ? "_over" : "_out"].call(this, event);
297 |
298 | // we just moved out of a greedy child
299 | if (parentInstance && c == 'isout') {
300 | parentInstance['isout'] = 0;
301 | parentInstance['isover'] = 1;
302 | parentInstance._over.call(parentInstance, event);
303 | }
304 | });
305 |
306 | },
307 | dragStop: function( draggable, event ) {
308 | draggable.element.parents( ":not(body,html)" ).unbind( "scroll.droppable" );
309 | //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
310 | if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
311 | }
312 | };
313 |
314 | })(jQuery);
315 |
--------------------------------------------------------------------------------
/nginx/static/js/jquery.ui.mouse.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery UI Mouse 1.8.18
3 | *
4 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5 | * Dual licensed under the MIT or GPL Version 2 licenses.
6 | * http://jquery.org/license
7 | *
8 | * http://docs.jquery.com/UI/Mouse
9 | *
10 | * Depends:
11 | * jquery.ui.widget.js
12 | */
13 | (function( $, undefined ) {
14 |
15 | var mouseHandled = false;
16 | $( document ).mouseup( function( e ) {
17 | mouseHandled = false;
18 | });
19 |
20 | $.widget("ui.mouse", {
21 | options: {
22 | cancel: ':input,option',
23 | distance: 1,
24 | delay: 0
25 | },
26 | _mouseInit: function() {
27 |
28 | var self = this;
29 |
30 | this.element
31 | .bind('mousedown.'+this.widgetName, function(event) {
32 | return self._mouseDown(event);
33 | })
34 | .bind('click.'+this.widgetName, function(event) {
35 | if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) {
36 | $.removeData(event.target, self.widgetName + '.preventClickEvent');
37 | event.stopImmediatePropagation();
38 | return false;
39 | }
40 | });
41 |
42 | this.started = false;
43 | },
44 |
45 | // TODO: make sure destroying one instance of mouse doesn't mess with
46 | // other instances of mouse
47 | _mouseDestroy: function() {
48 | this.element.unbind('.'+this.widgetName);
49 | },
50 |
51 | _mouseDown: function(event) {
52 | if (!possibles_to_move(event.srcElement.classList[1])) return;
53 |
54 | // don't let more than one widget handle mouseStart
55 | if( mouseHandled ) { return };
56 |
57 | // we may have missed mouseup (out of window)
58 | (this._mouseStarted && this._mouseUp(event));
59 |
60 | this._mouseDownEvent = event;
61 |
62 | var self = this,
63 | btnIsLeft = (event.which == 1),
64 | // event.target.nodeName works around a bug in IE 8 with
65 | // disabled inputs (#7620)
66 | elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
67 | if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
68 | return true;
69 | }
70 |
71 | this.mouseDelayMet = !this.options.delay;
72 | if (!this.mouseDelayMet) {
73 | this._mouseDelayTimer = setTimeout(function() {
74 | self.mouseDelayMet = true;
75 | }, this.options.delay);
76 | }
77 |
78 | if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
79 | this._mouseStarted = (this._mouseStart(event) !== false);
80 | if (!this._mouseStarted) {
81 | event.preventDefault();
82 | return true;
83 | }
84 | }
85 |
86 | // Click event may never have fired (Gecko & Opera)
87 | if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
88 | $.removeData(event.target, this.widgetName + '.preventClickEvent');
89 | }
90 |
91 | // these delegates are required to keep context
92 | this._mouseMoveDelegate = function(event) {
93 | return self._mouseMove(event);
94 | };
95 | this._mouseUpDelegate = function(event) {
96 | return self._mouseUp(event);
97 | };
98 | $(document)
99 | .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
100 | .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
101 |
102 | event.preventDefault();
103 |
104 | mouseHandled = true;
105 | return true;
106 | },
107 |
108 | _mouseMove: function(event) {
109 | // IE mouseup check - mouseup happened when mouse was out of window
110 | if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
111 | return this._mouseUp(event);
112 | }
113 |
114 | if (this._mouseStarted) {
115 | this._mouseDrag(event);
116 | return event.preventDefault();
117 | }
118 |
119 | if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
120 | this._mouseStarted =
121 | (this._mouseStart(this._mouseDownEvent, event) !== false);
122 | (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
123 | }
124 |
125 | return !this._mouseStarted;
126 | },
127 |
128 | _mouseUp: function(event) {
129 | $(document)
130 | .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
131 | .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
132 |
133 | if (this._mouseStarted) {
134 | this._mouseStarted = false;
135 |
136 | if (event.target == this._mouseDownEvent.target) {
137 | $.data(event.target, this.widgetName + '.preventClickEvent', true);
138 | }
139 |
140 | this._mouseStop(event);
141 | }
142 |
143 | return false;
144 | },
145 |
146 | _mouseDistanceMet: function(event) {
147 | return (Math.max(
148 | Math.abs(this._mouseDownEvent.pageX - event.pageX),
149 | Math.abs(this._mouseDownEvent.pageY - event.pageY)
150 | ) >= this.options.distance
151 | );
152 | },
153 |
154 | _mouseDelayMet: function(event) {
155 | return this.mouseDelayMet;
156 | },
157 |
158 | // These are placeholder methods, to be overriden by extending plugin
159 | _mouseStart: function(event) {},
160 | _mouseDrag: function(event) {},
161 | _mouseStop: function(event) {},
162 | _mouseCapture: function(event) { return true; }
163 | });
164 |
165 | })(jQuery);
166 |
--------------------------------------------------------------------------------
/nginx/static/js/jquery.ui.touch-punch.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery UI Touch Punch 0.2.3
3 | *
4 | * Copyright 2011–2014, Dave Furfero
5 | * Dual licensed under the MIT or GPL Version 2 licenses.
6 | *
7 | * Depends:
8 | * jquery.ui.widget.js
9 | * jquery.ui.mouse.js
10 | */
11 | !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);
--------------------------------------------------------------------------------
/nginx/static/js/jquery.ui.widget.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery UI Widget 1.8.18
3 | *
4 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5 | * Dual licensed under the MIT or GPL Version 2 licenses.
6 | * http://jquery.org/license
7 | *
8 | * http://docs.jquery.com/UI/Widget
9 | */
10 | (function( $, undefined ) {
11 |
12 | // jQuery 1.4+
13 | if ( $.cleanData ) {
14 | var _cleanData = $.cleanData;
15 | $.cleanData = function( elems ) {
16 | for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
17 | try {
18 | $( elem ).triggerHandler( "remove" );
19 | // http://bugs.jquery.com/ticket/8235
20 | } catch( e ) {}
21 | }
22 | _cleanData( elems );
23 | };
24 | } else {
25 | var _remove = $.fn.remove;
26 | $.fn.remove = function( selector, keepData ) {
27 | return this.each(function() {
28 | if ( !keepData ) {
29 | if ( !selector || $.filter( selector, [ this ] ).length ) {
30 | $( "*", this ).add( [ this ] ).each(function() {
31 | try {
32 | $( this ).triggerHandler( "remove" );
33 | // http://bugs.jquery.com/ticket/8235
34 | } catch( e ) {}
35 | });
36 | }
37 | }
38 | return _remove.call( $(this), selector, keepData );
39 | });
40 | };
41 | }
42 |
43 | $.widget = function( name, base, prototype ) {
44 | var namespace = name.split( "." )[ 0 ],
45 | fullName;
46 | name = name.split( "." )[ 1 ];
47 | fullName = namespace + "-" + name;
48 |
49 | if ( !prototype ) {
50 | prototype = base;
51 | base = $.Widget;
52 | }
53 |
54 | // create selector for plugin
55 | $.expr[ ":" ][ fullName ] = function( elem ) {
56 | return !!$.data( elem, name );
57 | };
58 |
59 | $[ namespace ] = $[ namespace ] || {};
60 | $[ namespace ][ name ] = function( options, element ) {
61 | // allow instantiation without initializing for simple inheritance
62 | if ( arguments.length ) {
63 | this._createWidget( options, element );
64 | }
65 | };
66 |
67 | var basePrototype = new base();
68 | // we need to make the options hash a property directly on the new instance
69 | // otherwise we'll modify the options hash on the prototype that we're
70 | // inheriting from
71 | // $.each( basePrototype, function( key, val ) {
72 | // if ( $.isPlainObject(val) ) {
73 | // basePrototype[ key ] = $.extend( {}, val );
74 | // }
75 | // });
76 | basePrototype.options = $.extend( true, {}, basePrototype.options );
77 | $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
78 | namespace: namespace,
79 | widgetName: name,
80 | widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
81 | widgetBaseClass: fullName
82 | }, prototype );
83 |
84 | $.widget.bridge( name, $[ namespace ][ name ] );
85 | };
86 |
87 | $.widget.bridge = function( name, object ) {
88 | $.fn[ name ] = function( options ) {
89 | var isMethodCall = typeof options === "string",
90 | args = Array.prototype.slice.call( arguments, 1 ),
91 | returnValue = this;
92 |
93 | // allow multiple hashes to be passed on init
94 | options = !isMethodCall && args.length ?
95 | $.extend.apply( null, [ true, options ].concat(args) ) :
96 | options;
97 |
98 | // prevent calls to internal methods
99 | if ( isMethodCall && options.charAt( 0 ) === "_" ) {
100 | return returnValue;
101 | }
102 |
103 | if ( isMethodCall ) {
104 | this.each(function() {
105 | var instance = $.data( this, name ),
106 | methodValue = instance && $.isFunction( instance[options] ) ?
107 | instance[ options ].apply( instance, args ) :
108 | instance;
109 | // TODO: add this back in 1.9 and use $.error() (see #5972)
110 | // if ( !instance ) {
111 | // throw "cannot call methods on " + name + " prior to initialization; " +
112 | // "attempted to call method '" + options + "'";
113 | // }
114 | // if ( !$.isFunction( instance[options] ) ) {
115 | // throw "no such method '" + options + "' for " + name + " widget instance";
116 | // }
117 | // var methodValue = instance[ options ].apply( instance, args );
118 | if ( methodValue !== instance && methodValue !== undefined ) {
119 | returnValue = methodValue;
120 | return false;
121 | }
122 | });
123 | } else {
124 | this.each(function() {
125 | var instance = $.data( this, name );
126 | if ( instance ) {
127 | instance.option( options || {} )._init();
128 | } else {
129 | $.data( this, name, new object( options, this ) );
130 | }
131 | });
132 | }
133 |
134 | return returnValue;
135 | };
136 | };
137 |
138 | $.Widget = function( options, element ) {
139 | // allow instantiation without initializing for simple inheritance
140 | if ( arguments.length ) {
141 | this._createWidget( options, element );
142 | }
143 | };
144 |
145 | $.Widget.prototype = {
146 | widgetName: "widget",
147 | widgetEventPrefix: "",
148 | options: {
149 | disabled: false
150 | },
151 | _createWidget: function( options, element ) {
152 | // $.widget.bridge stores the plugin instance, but we do it anyway
153 | // so that it's stored even before the _create function runs
154 | $.data( element, this.widgetName, this );
155 | this.element = $( element );
156 | this.options = $.extend( true, {},
157 | this.options,
158 | this._getCreateOptions(),
159 | options );
160 |
161 | var self = this;
162 | this.element.bind( "remove." + this.widgetName, function() {
163 | self.destroy();
164 | });
165 |
166 | this._create();
167 | this._trigger( "create" );
168 | this._init();
169 | },
170 | _getCreateOptions: function() {
171 | return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
172 | },
173 | _create: function() {},
174 | _init: function() {},
175 |
176 | destroy: function() {
177 | this.element
178 | .unbind( "." + this.widgetName )
179 | .removeData( this.widgetName );
180 | this.widget()
181 | .unbind( "." + this.widgetName )
182 | .removeAttr( "aria-disabled" )
183 | .removeClass(
184 | this.widgetBaseClass + "-disabled " +
185 | "ui-state-disabled" );
186 | },
187 |
188 | widget: function() {
189 | return this.element;
190 | },
191 |
192 | option: function( key, value ) {
193 | var options = key;
194 |
195 | if ( arguments.length === 0 ) {
196 | // don't return a reference to the internal hash
197 | return $.extend( {}, this.options );
198 | }
199 |
200 | if (typeof key === "string" ) {
201 | if ( value === undefined ) {
202 | return this.options[ key ];
203 | }
204 | options = {};
205 | options[ key ] = value;
206 | }
207 |
208 | this._setOptions( options );
209 |
210 | return this;
211 | },
212 | _setOptions: function( options ) {
213 | var self = this;
214 | $.each( options, function( key, value ) {
215 | self._setOption( key, value );
216 | });
217 |
218 | return this;
219 | },
220 | _setOption: function( key, value ) {
221 | this.options[ key ] = value;
222 |
223 | if ( key === "disabled" ) {
224 | this.widget()
225 | [ value ? "addClass" : "removeClass"](
226 | this.widgetBaseClass + "-disabled" + " " +
227 | "ui-state-disabled" )
228 | .attr( "aria-disabled", value );
229 | }
230 |
231 | return this;
232 | },
233 |
234 | enable: function() {
235 | return this._setOption( "disabled", false );
236 | },
237 | disable: function() {
238 | return this._setOption( "disabled", true );
239 | },
240 |
241 | _trigger: function( type, event, data ) {
242 | var prop, orig,
243 | callback = this.options[ type ];
244 |
245 | data = data || {};
246 | event = $.Event( event );
247 | event.type = ( type === this.widgetEventPrefix ?
248 | type :
249 | this.widgetEventPrefix + type ).toLowerCase();
250 | // the original event may come from any element
251 | // so we need to reset the target on the new event
252 | event.target = this.element[ 0 ];
253 |
254 | // copy original event properties over to the new event
255 | orig = event.originalEvent;
256 | if ( orig ) {
257 | for ( prop in orig ) {
258 | if ( !( prop in event ) ) {
259 | event[ prop ] = orig[ prop ];
260 | }
261 | }
262 | }
263 |
264 | this.element.trigger( event, data );
265 |
266 | return !( $.isFunction(callback) &&
267 | callback.call( this.element[0], event, data ) === false ||
268 | event.isDefaultPrevented() );
269 | }
270 | };
271 |
272 | })( jQuery );
273 |
--------------------------------------------------------------------------------
/nginx/static/js/player_timers.js:
--------------------------------------------------------------------------------
1 | var chessTimer;
2 |
3 | function updateFirstPlayerTime(first_player_time_var){
4 |
5 | var first_player_time = first_player_time_var;
6 | window.first_player_time = first_player_time;
7 | updateTimer(first_player_time, "first_player_time");
8 | }
9 |
10 | function updateSecondPlayerTime(second_player_time_var){
11 |
12 | var second_player_time = second_player_time_var;
13 | window.second_player_time = second_player_time;
14 | updateTimer(second_player_time, "second_player_time");
15 |
16 | if (first_player_time <= 0){
17 | clearInterval(chessTimer);
18 | }
19 | }
20 |
21 | function startTimers(){
22 | chessTimer = setInterval(function () {
23 | if (window.turn){
24 | updateFirstPlayerTime(window.first_player_time - 1);
25 | return;
26 | }
27 | updateSecondPlayerTime(window.second_player_time - 1);
28 | }, 1000);}
29 |
30 | function updateTimer(time, player){
31 |
32 | ifNegativeTime(time);
33 |
34 | watch = Math.floor(time / 60) + ":" + (time % 60);
35 |
36 | replaceText($('#' + player)[0].children[0], watch);
37 |
38 | }
39 |
40 | function UpdateTimerFunction(){
41 | if (window.turn){
42 | updateFirstPlayerTime(window.first_player_time - 1);
43 | return;
44 | }
45 | updateSecondPlayerTime(window.second_player_time - 1);
46 | }
47 |
48 | function ifNegativeTime(player_time){
49 | if (player_time < 0) {
50 |
51 | string = window.location.pathname.slice(0, -1);
52 |
53 | tag = string.slice(string.lastIndexOf('/')+1);
54 |
55 | console.log(tag);
56 |
57 | $.ajax(
58 | {
59 | type: 'POST',
60 | url: `${config.ssl_http}://${config.domain} /api/v1/check_timer?tag=${tag}`
61 | });
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/nginx/static/js/prepare_time.js:
--------------------------------------------------------------------------------
1 | function startPrepareTimer(prepare_time_var){
2 |
3 | if (prepare_time_var <= 0) {
4 | clearPrepareDiv();
5 | return false;}
6 |
7 | var prepare_time_timer = window.setInterval(function(){
8 | if (prepare_time <= 0) {
9 | window.clearInterval(prepare_time_timer);
10 | clearPrepareDiv();
11 | startTimers();
12 | return false;}
13 |
14 | updatePrepareTime(window.prepare_time - 1);
15 | }, 1000);}
16 |
17 |
18 | function clearPrepareDiv(){
19 | document.getElementById('prepare_time_div').remove();
20 | }
21 |
22 | startPrepareTimer(prepare_time)
--------------------------------------------------------------------------------
/nginx/static/js/websocket.js:
--------------------------------------------------------------------------------
1 | function webSocketConnector(){
2 |
3 | let game_tag = window.location.pathname.split('/').at(-2);
4 |
5 | let connect = new WebSocket(`${config.ssl_ws}://${config.domain}/websocket/games/` + game_tag + "/");
6 |
7 | return connect;
8 | }
9 |
10 |
11 | function onMessage(event){
12 | let response = $.parseJSON(event.data);
13 |
14 | console.log(response);
15 |
16 | switch(response["event"]){
17 |
18 | case "draw_offer":
19 | if (Number(response["recipient"]) == user_id){
20 | if(!draw_offer){
21 | $('#draw_offer_modal').css({"visibility": "visible"})}
22 | }
23 | break;
24 |
25 | case "error":
26 | alert(response["message"] + "Если это не так, скинь скрин этой ошибки - @Justiks1337");
27 | break;
28 |
29 | case "illegal_move_error":
30 | replaceText(document.getElementById('illegal_move_message'), response["message"]);
31 | $("#illegal_move_modal").css({"visibility": "visible"})
32 | window.c.updateMatrixPosition(window.board_fen)
33 | break;
34 |
35 | case "end_game":
36 | replaceText(document.getElementById('end_game_message'), response["message"]);
37 | $("#end_game_modal").css({"visibility": "visible"})
38 | window.clearInterval(window.chessTimer);
39 | break;
40 |
41 | case "on_check":
42 | if (response["recipient"] == user_id){
43 | $('#on_check_modal').css({"visibility": "visible"})
44 | }
45 |
46 | break;
47 |
48 | case "update_board":
49 | window.c.updateMatrixPosition(response["board"]);
50 | window.board_fen = response["board"];
51 |
52 | window.onMoveSound();
53 |
54 | if (window.turn){
55 | updateTurn("false");
56 | }
57 | else {updateTurn("true");}
58 |
59 | updateFirstPlayerTime(response["first_user_time"]);
60 | updateSecondPlayerTime(response["second_user_time"]);
61 | break;
62 |
63 | case "legal_moves":
64 | showLegalMoves(response["cells"]);
65 | break;
66 | }
67 |
68 |
69 | }
70 |
71 | var socket_connection = webSocketConnector();
72 |
73 | function move(start_cell, end_cell){
74 |
75 | socket_connection.send(JSON.stringify({
76 | "type": "move", "start_cell": start_cell.getElementsByClassName('squareNotation')[0].innerText,
77 | "end_cell": end_cell.getElementsByClassName('squareNotation')[0].innerText}));
78 | }
79 |
80 | function getLegalMoves(figure_cell){
81 | socket_connection.send(JSON.stringify({
82 | "type": "get_legal_moves", "figure_cell": figure_cell}))
83 | }
84 |
85 | socket_connection.onmessage = onMessage;
--------------------------------------------------------------------------------
/nginx/static/js/websocket_spectator.js:
--------------------------------------------------------------------------------
1 | function webSocketConnector(){
2 |
3 | let game_tag = window.location.pathname.split('/').at(-2);
4 |
5 | let connect = new WebSocket(`${config.ssl_ws}://${config.domain}/websocket/games/` + game_tag + "/");
6 |
7 | return connect;
8 | }
9 |
10 |
11 | function onMessage(event){
12 | let response = $.parseJSON(event.data);
13 |
14 | switch(response["event"]){
15 |
16 | case "end_game":
17 | replaceText(document.getElementById('end_game_message'), response["message"]);
18 | $("#end_game_modal").css({"visibility": "visible"})
19 | window.clearInterval(window.chessTimer);
20 | break;
21 |
22 | case "update_board":
23 | window.c.updateMatrixPosition(response["board"]);
24 | window.board_fen = response["board"];
25 | if (window.turn){
26 | updateTurn("false");
27 | }
28 | else {updateTurn("true");}
29 |
30 | updateFirstPlayerTime(response["first_user_time"]);
31 | updateSecondPlayerTime(response["second_user_time"]);
32 | break;
33 | }
34 | }
35 |
36 | var socket_connection = webSocketConnector();
37 |
38 | function timeEnd(){
39 | socket_connection.send(JSON.stringify({"type": "end_timer"}))
40 | }
41 |
42 |
43 |
44 | socket_connection.onmessage = onMessage;
--------------------------------------------------------------------------------
/previews/achievement_1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/previews/achievement_1
--------------------------------------------------------------------------------
/previews/game:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/previews/game
--------------------------------------------------------------------------------
/previews/game_ready:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/previews/game_ready
--------------------------------------------------------------------------------
/telegram/QueueClass.py:
--------------------------------------------------------------------------------
1 | from asyncio import create_task
2 | from queue import Queue as PyQueue
3 |
4 | from aiohttp import ClientSession
5 |
6 | from config.ConfigValues import ConfigValues
7 | from decorators import send_message
8 |
9 |
10 | class Queue(PyQueue):
11 | """Класс - очередь (единственный экземпляр создаётся в __init__.py"""
12 |
13 | # rewriting implementation
14 | def __init__(self, maxsize):
15 | super().__init__(maxsize)
16 | self.queue: set = set()
17 |
18 | def _put(self, item):
19 | self.queue.add(item)
20 |
21 | def _get(self):
22 | return self.queue.pop()
23 |
24 | async def add_new_user(self, user_id: int):
25 | """Добавляет нового игрока в очередь"""
26 |
27 | try:
28 | await self.checks(user_id)
29 | except AssertionError as error:
30 | await send_message(user_id, error.args[0])
31 | return
32 |
33 | self.put(user_id)
34 |
35 | send_task = create_task(send_message(user_id, ConfigValues.on_queue_join_message))
36 | new_user_task = create_task(self.on_new_user())
37 |
38 | await new_user_task
39 | await send_task
40 |
41 | def leave_from_queue(self, user_id: int):
42 | """Удаляет участника из очереди"""
43 |
44 | self.queue.remove(user_id)
45 |
46 | async def on_new_user(self):
47 | """Хандлер срабатывающий при попадании нового пользователя в очередь"""
48 | if self.full():
49 | await self.start_game()
50 |
51 | async def start_game(self):
52 | """Начало игры """
53 |
54 | users = [self.get(), self.get()]
55 |
56 | async with ClientSession() as session:
57 | async with session.post(
58 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/start_game",
59 | headers={
60 | "content-type": "application/json",
61 | "Authorization": ConfigValues.server_authkey},
62 | json={"players": [users[0], users[1]]}) as response:
63 | json = await response.json()
64 |
65 | url = f"{ConfigValues.proxy_http_protocol}://{ConfigValues.proxy_ip}\
66 | {ConfigValues.url_to_playground.replace('{rout}', json['uuid'])}"
67 |
68 | for user_id in users:
69 | await send_message(user_id, ConfigValues.on_find_enemy.replace('{url}', url))
70 |
71 | async def checks(self, user_id):
72 | self.check_in_queue(user_id)
73 | await Queue.check_in_game(user_id)
74 |
75 | def check_in_queue(self, user_id):
76 | assert user_id not in self.queue, ConfigValues.if_in_queue
77 |
78 | @staticmethod
79 | async def check_in_game(user_id):
80 | async with ClientSession() as session:
81 | async with session.post(
82 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/check_in_game",
83 | params={"user_id": user_id},
84 | headers={
85 | "content-type": "application/json",
86 | "Authorization": ConfigValues.server_authkey}) as response:
87 | json = await response.json()
88 | print(json)
89 | assert not json['in_game'], ConfigValues.in_game_error
90 |
91 |
92 | main_queue = Queue(2)
93 |
--------------------------------------------------------------------------------
/telegram/__init__.py:
--------------------------------------------------------------------------------
1 | from aiogram import Dispatcher, Bot
2 |
3 | from config.ConfigValues import ConfigValues
4 |
5 | bot: Bot = Bot(token=ConfigValues.telegram_token)
6 | dp = Dispatcher()
7 |
--------------------------------------------------------------------------------
/telegram/authorization_core.py:
--------------------------------------------------------------------------------
1 | import os
2 | import aiohttp
3 | from aiogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup
4 | from aiohttp.client import ClientSession
5 |
6 | from config.ConfigValues import ConfigValues
7 | from decorators import send_message
8 | from telegram import bot
9 |
10 |
11 | async def download_user_avatar(user_id: int):
12 | """Download user profile photo
13 |
14 | :arg user_id - user id
15 | """
16 |
17 | async with aiohttp.ClientSession() as session:
18 | async with session.get(f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/in_database",
19 | params={"user_id": user_id},
20 | headers={"Content-type": "application/json",
21 | "Authorization": ConfigValues.server_authkey}) as response:
22 | json = await response.json()
23 | if json.get("in_database"):
24 | return
25 |
26 | user_profile_photo = await bot.get_user_profile_photos(user_id)
27 |
28 | if len(user_profile_photo.photos) > 0:
29 | file = await bot.get_file(user_profile_photo.photos[0][0].file_id)
30 | file_destination = get_destination(user_id, get_file_name(file.file_path))
31 | await bot.download_file(file.file_path, file_destination)
32 |
33 | with open(file_destination, "rb") as destination:
34 | async with aiohttp.ClientSession() as session:
35 | async with session.post(
36 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/add_avatar?file_name={file_destination}",
37 | data={"file": destination},
38 | headers={"Authorization": ConfigValues.server_authkey}):
39 | os.remove(file_destination)
40 |
41 |
42 | def get_file_name(file_path: str) -> str:
43 | return file_path[-file_path[::-1].index('/')]
44 |
45 |
46 | def get_file_format(file_name: str) -> str:
47 | try:
48 | return file_name[file_name.index('.'):]
49 | except ValueError:
50 | return ".png"
51 |
52 |
53 | def get_destination(user_id: int, file_name) -> str:
54 | file_format = get_file_format(file_name)
55 |
56 | file_destination = str(user_id) + file_format
57 |
58 | return file_destination
59 |
60 |
61 | async def send_auth_message(message: Message):
62 | user_id = message.from_user.id
63 |
64 | username = message.from_user.username
65 | nickname = message.from_user.full_name
66 |
67 | if not username:
68 | username = nickname
69 |
70 | async with ClientSession() as session:
71 | async with session.post(
72 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/new_token",
73 | params={"user_id": user_id,
74 | "username": username,
75 | "nickname": nickname},
76 | headers={"content-type": "application/json",
77 | "Authorization": ConfigValues.server_authkey}) as response:
78 | json = dict(await response.json())
79 | if json['success']:
80 |
81 | button = InlineKeyboardButton(
82 | text="Авторизуйся!",
83 | url=f"{ConfigValues.proxy_http_protocol}://{ConfigValues.proxy_ip}/?token={json['token']}")
84 | markup = InlineKeyboardMarkup(inline_keyboard=[[button]])
85 |
86 | await message.reply(ConfigValues.authorization_message, reply_markup=markup)
87 | return
88 |
89 | await send_message(message.chat.id, ConfigValues.on_authorization_error)
90 |
91 |
--------------------------------------------------------------------------------
/telegram/config/ConfigValues.py:
--------------------------------------------------------------------------------
1 | from configparser import ConfigParser as _ConfigParser
2 | import os
3 |
4 |
5 | class ConfigValues:
6 | """Get values from config"""
7 |
8 | __config_file = _ConfigParser()
9 | __config_file.read(os.path.join(os.path.dirname(__file__), 'config.ini'), encoding='utf-8-sig')
10 |
11 | # database section
12 | database_user = __config_file.get('database', 'database_user')
13 | database_password = __config_file.get('database', 'database_password')
14 | database_name = __config_file.get('database', 'database_name')
15 | database_host = __config_file.get('database', 'database_host')
16 | database_port = int(__config_file.get('database', 'database_port'))
17 |
18 | # Telegram section
19 | telegram_token = __config_file.get('Telegram', 'telegram_token')
20 | admin_ids: list = __config_file.get('Telegram', 'admin_ids').split(', ')
21 | recharge_time = int(__config_file.get('Telegram', 'recharge_time'))
22 | authorization_tokens_live_time = int(__config_file.get('Telegram', 'authorization_tokens_live_time'))
23 |
24 | # Messages
25 | authorization_instructions = __config_file.get('Messages', 'authorization_instructions').replace('\\n', '\n')
26 | game_instructions = __config_file.get('Messages', 'game_instructions').replace('\\n', '\n')
27 | manual = __config_file.get('Messages', 'manual').replace('\\n', '\n')
28 | on_queue_join_message = __config_file.get('Messages', 'on_queue_join_message').replace('\\n', '\n')
29 | on_queue_leave_message = __config_file.get('Messages', 'on_queue_leave_message').replace('\\n', '\n')
30 | only_in_dm_message = __config_file.get('Messages', 'only_in_dm_message').replace('\\n', '\n')
31 | if_in_queue = __config_file.get('Messages', 'if_in_queue').replace('\\n', '\n')
32 | if_not_in_queue = __config_file.get('Messages', 'if_not_in_queue').replace('\\n', '\n')
33 | if_games_not_enough = __config_file.get('Messages', 'if_games_not_enough').replace('\\n', '\n')
34 | in_game_error = __config_file.get('Messages', 'in_game_error').replace('\\n', '\n')
35 | profile_message = __config_file.get('Messages', 'profile_message').replace('\\n', '\n')
36 | dashboard_title = __config_file.get('Messages', 'dashboard_title').replace('\\n', '\n')
37 | dashboard_object = __config_file.get('Messages', 'dashboard_object').replace('\\n', '\n')
38 | dashboard_on_range_error = __config_file.get('Messages', 'dashboard_on_range_error').replace('\\n', '\n')
39 | illegal_move_error = __config_file.get('Messages', 'illegal_move_error').replace('\\n', '\n')
40 | on_mate_message = __config_file.get('Messages', 'on_mate_message').replace('\\n', '\n')
41 | on_stalemate_message = __config_file.get('Messages', 'on_stalemate_message').replace('\\n', '\n')
42 | on_draw_message = __config_file.get('Messages', 'on_draw_message').replace('\\n', '\n')
43 | on_end_time = __config_file.get('Messages', 'on_end_time').replace('\\n', '\n')
44 | on_someone_move = __config_file.get('Messages', 'on_someone_move').replace('\\n', '\n')
45 | on_resign = __config_file.get('Messages', 'on_resign').replace('\\n', '\n')
46 | authorization_message = __config_file.get('Messages', 'authorization_message').replace('\\n', '\n')
47 | success_authorization_message = __config_file.get('Messages', 'success_authorization_message').replace('\\n', '\n')
48 | on_authorization_error = __config_file.get('Messages', 'on_authorization_error').replace('\\n', '\n')
49 | on_delete_authorization_code = __config_file.get('Messages', 'on_delete_authorization_code').replace('\\n', '\n')
50 | on_unsuccessful_authorization_message = __config_file.get('Messages', 'on_unsuccessful_authorization_message').replace('\\n', '\n')
51 | unauthorized_message = __config_file.get('Messages', 'unauthorized_message').replace('\\n', '\n')
52 | on_blacklist_message = __config_file.get('Messages', 'on_blacklist_message').replace('\\n', '\n')
53 | on_is_not_admin = __config_file.get('Messages', 'on_is_not_admin').replace('\\n', '\n')
54 | successful_add_to_blacklist = __config_file.get('Messages', 'successful_add_to_blacklist').replace('\\n', '\n')
55 | successful_remove_from_blacklist = __config_file.get('Messages', 'successful_remove_from_blacklist').replace('\\n', '\n')
56 | on_invalid_args = __config_file.get('Messages', 'on_invalid_args').replace('\\n', '\n')
57 | on_find_enemy = __config_file.get('Messages', 'on_find_enemy').replace('\\n', '\n')
58 | in_recharge = __config_file.get('Messages', 'in_recharge').replace('\\n', '\n')
59 | on_illegal_action_error = __config_file.get('Messages', 'on_illegal_action_error').replace('\\n', '\n')
60 |
61 | # Game
62 |
63 | prepare_time = int(__config_file.get('Game', 'prepare_time'))
64 | game_time = int(__config_file.get('Game', 'game_time'))
65 |
66 | # Web
67 |
68 | path_to_avatars = __config_file.get('Web', 'path_to_avatars')
69 | url_to_playground = __config_file.get('Web', 'url_to_playground').replace('\\n', '\n')
70 | bot_websocket = __config_file.get('Web', 'bot_websocket').replace('\\n', '\n')
71 | server_ip = __config_file.get('Web', 'server_ip').replace('\\n', '\n')
72 | proxy_ip = __config_file.get('Web', 'proxy_ip').replace('\\n', '\n')
73 | server_port = int(__config_file.get('Web', 'server_port'))
74 | server_http_protocol = __config_file.get('Web', 'server_http_protocol')
75 | proxy_http_protocol = __config_file.get('Web', 'proxy_http_protocol')
76 | websocket_protocol = __config_file.get('Web', 'websocket_protocol')
77 | server_authkey = __config_file.get('Web', 'server_authkey')
78 |
--------------------------------------------------------------------------------
/telegram/config/config.ini:
--------------------------------------------------------------------------------
1 | [database]
2 | database_user=chess
3 | database_password=12345678
4 | database_name=chess_database
5 | database_host=database
6 | database_port=5432
7 |
8 | [Telegram]
9 | recharge_time = 5
10 | telegram_token =
11 | admin_ids = 408895166, 2121622736
12 | authorization_tokens_live_time = 300
13 |
14 | [Messages]
15 | authorization_instructions = Для начала игры авторизуйся! \n\nИспользуй команду /authorization и следуй иструкциям в появившемся сообщении
16 | game_instructions = Для регистрации на турнире используй команду /authorization, а после следуй инструкциям в появившемся сообщении. \n\nЕсли ты ввел все правильно, ты увидишь на экране сообщение об этом, твой телеграм аккаунт свяжется с сайтом и ты сможешь играть! По остальным вопросам - /help
17 | manual = После авторизации на сайте ты можешь начинать играть. \n\nДля этого отправь боту команду /play и наберись терпения, бот начнет искать тебе противника. \n\nКак только противник будет найден ты получишь оповещение в чате и ссылку на игру. После этого у тебя будет 30 секунд на то, что бы перейти по ссылке и приготовиться к игре. Далее действуй как желаешь нужным. \n \n По вопросам: @Elvira1C @Justiks1337 @Danya_501
18 | on_queue_join_message = Ожидание противника. Когда противник будет найден, бот оповестит тебя о начале игры. \n\nДля выхода из очереди используй команду /leave
19 | on_queue_leave_message = Ты покинул очередь, можешь перестать волноваться за свой рейтинг.
20 | only_in_dm_message = Эта команда доступна только в личных сообщениях бота!
21 | if_in_queue = Ты уже находишься в очереди, ожидай противника!
22 | if_not_in_queue = Ты не находишься в очереди.
23 | if_games_not_enough = У тебя недостаточно игр (0). Дождись следующего тура что бы продолжить играть! Если ты конечно прошел в него...
24 | in_game_error = Ты уже находишься в игре!
25 | profile_message = Твой профиль 📊📈: \n\n Очков: {points_amount} 💠
26 | dashboard_title = 🏆⭐️ Топ {amount} по победам в шахматах: \n\n
27 | dashboard_object = {position}. {player_name}: {points_amount} очков \n
28 | dashboard_on_range_error = Ты указал неверный диапазон участников! (Всего {amount} участников)
29 | illegal_move_error = Такой ход невозможен!
30 | on_mate_message = Мат! Победил игрок играющий за {color}ый цвет!
31 | on_stalemate_message = Пат! Ничья, победила дружба!
32 | on_draw_message = Игра окончена! Ничья, победила дружба!
33 | on_end_time = У игрока играющего за {color}ый цвет не осталось времени. Победа игрока играющего за противоположный цвет!
34 | on_someone_move = Сейчас ход другого игрока! Играем в порядке живой очереди!
35 | on_resign = Победа игрока за {color}ый цвет, ведь его противник сдался!
36 | authorization_message = Для авторизации нажми на кнопку под сообщением!
37 | success_authorization_message = Ты успешно авторизовался, теперь можешь идти играть!
38 | on_authorization_error = Ошибка авторизации! Возможные причины: \n\n 1). У вас уже есть токен авторизации \n\n 2). Вас нету в базе данных.
39 | on_delete_authorization_code = Вы не успели авторизоваться за 5 минут. Ваш токен авторизации был удалён, попробуй снова!
40 | on_unsuccessful_authorization_message = Такого кода авторизации не существует!
41 | unauthorized_message = Для использования этой команды авторизуйтесь! /start
42 | on_blacklist_message = Ты находишься в чёрном списке и не можешь использовать какие либо команды. За разблокировкой пиши - @Elvira1C
43 | on_is_not_admin = Для того что бы использовать эту команду ты должен находиться в списке администраторов!
44 | successful_add_to_blacklist = Пользователь @{username} успешно добавлен в чёрный список!
45 | successful_remove_from_blacklist = Пользователь успешно изъят из чёрного списка!
46 | on_invalid_args = Указан недопустимый аргумент/Недостаточно аргументов. Уточните синтаксис команды перед использованием.
47 | on_find_enemy = Противник найден! Ссылка на игру: \n{url}
48 | in_recharge = Не спешите с вводом команд! Подождите некоторое время
49 | on_illegal_action_error = Не пытайся сломать систему!
50 |
51 | [Game]
52 | prepare_time = 30
53 | game_time = 900
54 | games_amount = 5
55 |
56 | [Web]
57 | path_to_avatars = ../web_django/static/avatars/
58 | url_to_playground = /playgrounds/games/{rout}/
59 | bot_websocket = websocket/bot_consumer
60 | server_ip = daphne:8000
61 | proxy_ip = 127.0.0.1
62 | server_port = 8000
63 | server_http_protocol = http
64 | proxy_http_protocol = http
65 | websocket_protocol = ws
66 | server_authkey = 419a4997-0107-490e-bb83-2538890d8bb7
--------------------------------------------------------------------------------
/telegram/decorators.py:
--------------------------------------------------------------------------------
1 | from asyncio import sleep, create_task
2 |
3 | import aiogram
4 | import aiohttp
5 |
6 | from config.ConfigValues import ConfigValues
7 | from telegram import dp, bot
8 |
9 |
10 | def in_blacklist(func):
11 | """Check user in blacklist"""
12 |
13 | async def wrapped(message: aiogram.types.Message):
14 | async with aiohttp.ClientSession() as session:
15 | async with session.get(
16 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/blacklist/in_blacklist",
17 | params={"user_id": message.from_user.id},
18 | headers={"Content-type": "application/json",
19 | "Authorization": ConfigValues.server_authkey}) as response:
20 | jsn = await response.json()
21 | if jsn.get("in_blacklist"):
22 | return await func(message)
23 | return await send_message(message.chat.id, ConfigValues.on_blacklist_message)
24 | return wrapped
25 |
26 |
27 | def authorize(func):
28 | async def wrapper(message: aiogram.types.Message):
29 | async with aiohttp.ClientSession() as session:
30 | async with session.get(
31 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/in_database",
32 | params={"user_id": message.from_user.id},
33 | headers={"Content-type": "application/json",
34 | "Authorization": ConfigValues.server_authkey}) as response:
35 | jsn = await response.json()
36 | if jsn.get("in_database"):
37 | return await func(message)
38 | await send_message(message.chat.id, ConfigValues.unauthorized_message)
39 | return wrapper
40 |
41 |
42 | def in_admins(func):
43 | """Check user in admins"""
44 | async def wrapped(message):
45 | if str(message.from_user.id) in ConfigValues.admin_ids:
46 | return await func(message)
47 |
48 | return await send_message(message.chat.id, ConfigValues.on_is_not_admin)
49 |
50 | return wrapped
51 |
52 |
53 | def recharge(func):
54 | async def wrapper(message):
55 | if message.from_user.id in users_in_recharge:
56 | return await send_message(message.chat.id, ConfigValues.in_recharge)
57 |
58 | users_in_recharge.append(message.from_user.id)
59 | sleep_task = create_task(clear_recharge(message.from_user.id))
60 | func_task = create_task(func(message))
61 | await sleep_task
62 | await func_task
63 |
64 | return wrapper
65 |
66 |
67 | def only_in_dm(coro):
68 | async def wrapper(message: aiogram.types.Message):
69 | if message.from_user.id != message.chat.id:
70 | return await send_message(message.chat.id, ConfigValues.only_in_dm_message)
71 |
72 | await coro(message)
73 |
74 | return wrapper
75 |
76 |
77 | def command_handler(command):
78 | def decorator(coro):
79 | @dp.message(command)
80 | @only_in_dm
81 | @recharge
82 | @authorize
83 | @in_blacklist
84 | async def wrapper(message: aiogram.types.Message):
85 | await coro(message)
86 |
87 | return wrapper
88 | return decorator
89 |
90 |
91 | async def clear_recharge(user_id: int):
92 | await sleep(ConfigValues.recharge_time)
93 | users_in_recharge.remove(user_id)
94 |
95 |
96 | async def send_message(chat_id: int, text: str, *args, **kwargs):
97 | await bot.send_message(chat_id, text, *args, **kwargs)
98 |
99 |
100 | users_in_recharge = []
101 |
--------------------------------------------------------------------------------
/telegram/main.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | from telegram_log.log import log
4 | from telegram import dp, bot
5 |
6 | import telegram_admin
7 | import telegram_authorize
8 | import telegram_dashboard
9 | import telegram_help
10 | import telegram_profile
11 | import telegram_queue
12 | import telegram_start
13 |
14 |
15 | log.info('bot successful started!')
16 |
17 |
18 | async def main():
19 |
20 | await dp.start_polling(bot)
21 |
22 |
23 | if __name__ == '__main__':
24 | loop = asyncio.get_event_loop()
25 | loop.run_until_complete(main())
26 |
--------------------------------------------------------------------------------
/telegram/requirements.txt:
--------------------------------------------------------------------------------
1 | aiofiles==24.1.0
2 | aiogram==3.20.0.post0
3 | aiohappyeyeballs==2.6.1
4 | aiohttp==3.11.18
5 | aiosignal==1.3.2
6 | annotated-types==0.7.0
7 | attrs==25.3.0
8 | certifi==2025.4.26
9 | frozenlist==1.6.0
10 | idna==3.10
11 | magic-filter==1.0.12
12 | multidict==6.4.4
13 | propcache==0.3.1
14 | pydantic==2.11.5
15 | pydantic_core==2.33.2
16 | typing-inspection==0.4.1
17 | typing_extensions==4.13.2
18 | yarl==1.20.0
19 |
--------------------------------------------------------------------------------
/telegram/telegram_admin.py:
--------------------------------------------------------------------------------
1 | import aiohttp
2 | from aiogram.types import Message
3 | from aiogram.filters import Command
4 |
5 | from config.ConfigValues import ConfigValues
6 | from decorators import in_admins, command_handler, send_message
7 |
8 |
9 | @command_handler(Command('add_on_blacklist'))
10 | @in_admins
11 | async def add_on_blacklist(message: Message):
12 | user_id = int(message.get_args())
13 | async with aiohttp.ClientSession() as session:
14 | async with session.post(
15 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/blacklist/add_to_blacklist",
16 | params={"user_id": user_id},
17 | headers={
18 | "content-type": "application/json",
19 | "Authorization": ConfigValues.server_authkey}):
20 | await send_message(message.chat.id, ConfigValues.successful_add_to_blacklist.replace('{username}', user_id))
21 |
22 |
23 | @command_handler(Command('remove_from_blacklist'))
24 | @in_admins
25 | async def remove_from_blacklist(message: Message):
26 | user_id = int(message.get_args())
27 |
28 | async with aiohttp.ClientSession() as session:
29 | async with session.post(
30 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/blacklist/remove_from_blacklist",
31 | params={"user_id": user_id},
32 | headers={"Content-type": "application/json",
33 | "Authorization": ConfigValues.server_authkey}):
34 | await send_message(message.chat.id, ConfigValues.successful_remove_from_blacklist)
35 |
36 |
--------------------------------------------------------------------------------
/telegram/telegram_authorize.py:
--------------------------------------------------------------------------------
1 | from aiogram.filters import Command
2 |
3 | from decorators import recharge, in_blacklist, only_in_dm
4 | from authorization_core import send_auth_message
5 | from telegram import dp
6 |
7 |
8 | @dp.message(Command("authorization"))
9 | @recharge
10 | @only_in_dm
11 | @in_blacklist
12 | async def new_token(message):
13 | await send_auth_message(message)
14 |
15 |
--------------------------------------------------------------------------------
/telegram/telegram_dashboard.py:
--------------------------------------------------------------------------------
1 | import aiohttp
2 | from aiogram.types import Message
3 | from aiogram.exceptions import TelegramForbiddenError
4 | from aiogram.filters import Command
5 |
6 | from config.ConfigValues import ConfigValues
7 | from decorators import command_handler, send_message
8 |
9 |
10 | @command_handler(Command('top'))
11 | async def get_top(message: Message):
12 | """send message with dashboard"""
13 |
14 | count = message.text.split()[-1]
15 |
16 | amount = 0
17 |
18 | if not count:
19 | amount = 10
20 | elif count.isdigit():
21 | amount = int(count)
22 | elif count == "all":
23 | amount = 0
24 |
25 | async with aiohttp.ClientSession() as session:
26 | async with session.get(
27 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/dashboard",
28 | params={"count": amount},
29 | headers={"Content-type": "application/json",
30 | "Authorization": ConfigValues.server_authkey}) as response:
31 | top = await response.json()
32 |
33 | msg: str = ConfigValues.dashboard_title.replace('{amount}', str(amount))
34 |
35 | position = 0
36 | for player in top:
37 | try:
38 |
39 | player_name = player.get("nickname")
40 |
41 | position += 1
42 |
43 | msg = msg + ConfigValues.dashboard_object.replace(
44 | '{position}', str(position)).replace(
45 | '{player_name}', f'{player_name}').replace(
46 | '{points_amount}', str(player.get("points")))
47 |
48 | except TelegramForbiddenError:
49 | continue
50 |
51 | await send_message(message.chat.id, msg, parse_mode="html")
52 |
53 |
--------------------------------------------------------------------------------
/telegram/telegram_help.py:
--------------------------------------------------------------------------------
1 | from aiogram.types import Message
2 | from aiogram.filters import Command
3 |
4 | from decorators import recharge, send_message
5 | from config.ConfigValues import ConfigValues
6 | from telegram import dp
7 |
8 |
9 | @dp.message(Command('help'))
10 | @recharge
11 | async def send_manual(message: Message):
12 | await send_message(message.chat.id, ConfigValues.manual)
13 |
--------------------------------------------------------------------------------
/telegram/telegram_log/log.py:
--------------------------------------------------------------------------------
1 | from logging import getLogger, FileHandler, Formatter
2 | from os import path
3 |
4 | log = getLogger('telegram_log')
5 | log.setLevel('DEBUG')
6 |
7 | log_handler = FileHandler(path.join(path.dirname(__file__), 'main.log'), 'a+')
8 | log_handler.setFormatter(Formatter("%(name)s %(asctime)s %(levelname)s %(message)s"))
9 |
10 |
11 | log.addHandler(log_handler)
12 |
13 |
--------------------------------------------------------------------------------
/telegram/telegram_profile.py:
--------------------------------------------------------------------------------
1 | import aiohttp
2 | from aiogram.types import Message
3 | from aiogram.filters import Command
4 |
5 | from decorators import command_handler, send_message
6 | from config.ConfigValues import ConfigValues
7 |
8 |
9 | @command_handler(Command('profile'))
10 | async def profile(message: Message):
11 | """Отправляет в чат статистику пользователя"""
12 |
13 | async with aiohttp.ClientSession() as session:
14 | async with session.get(
15 | f"{ConfigValues.server_http_protocol}://{ConfigValues.server_ip}/api/v1/profile/{message.from_user.id}/",
16 | headers={"Content-type": "application/json",
17 | "Authorization": ConfigValues.server_authkey}) as response:
18 | stats_values = await response.json()
19 |
20 | await send_message(
21 | message.chat.id,
22 | ConfigValues.profile_message.replace('{points_amount}', str(stats_values["points"])))
23 |
--------------------------------------------------------------------------------
/telegram/telegram_queue.py:
--------------------------------------------------------------------------------
1 | from aiogram.types import Message
2 | from aiogram.filters import Command
3 |
4 | from QueueClass import main_queue as queue
5 | from config.ConfigValues import ConfigValues
6 | from decorators import command_handler, send_message, authorize
7 |
8 |
9 | @command_handler(Command('play'))
10 | @authorize
11 | async def queue_join(message: Message):
12 | """Добавляет пользователя в очередь"""
13 |
14 | await queue.add_new_user(message.from_user.id)
15 |
16 |
17 | @command_handler(Command('leave'))
18 | @authorize
19 | async def queue_leave(message: Message):
20 | """Удаляет пользователя из очереди"""
21 |
22 | try:
23 | queue.leave_from_queue(message.from_user.id)
24 | except KeyError:
25 | await send_message(message.chat.id, ConfigValues.if_not_in_queue)
26 | return
27 |
28 | await send_message(message.chat.id, ConfigValues.on_queue_leave_message)
29 |
30 |
31 | async def send_url_to_playground(user_id: int, uuid):
32 | """send url to game"""
33 |
34 | await send_message(
35 | user_id,
36 | ConfigValues.on_find_enemy.replace('{url}', ConfigValues.url_to_playground.replace('{rout}', uuid)))
37 |
--------------------------------------------------------------------------------
/telegram/telegram_start.py:
--------------------------------------------------------------------------------
1 | from aiogram.types import Message
2 | from aiogram.filters import Command
3 |
4 | from decorators import in_blacklist, recharge, only_in_dm
5 | from authorization_core import download_user_avatar, send_auth_message
6 | from telegram import dp
7 |
8 |
9 | @dp.message(Command('start'))
10 | @recharge
11 | @only_in_dm
12 | @in_blacklist
13 | async def start(message: Message):
14 | """send start message"""
15 |
16 | await send_auth_message(message)
17 | await download_user_avatar(message.from_user.id)
18 |
--------------------------------------------------------------------------------
/web_django/.env:
--------------------------------------------------------------------------------
1 | DATABASE_USER=chess
2 | DATABASE_PASSWORD=12345678
3 | DATABASE_NAME=chess_database
4 | DATABASE_HOST=database
5 | DATABASE_PORT=5432
6 |
7 | ILLEGAL_MOVE_ERROR="Такой ход невозможен!"
8 | ON_MATE_MESSAGE="Мат! Победил игрок играющий за {color}ый цвет!"
9 | ON_STALEMATE_MESSAGE="Пат! Ничья, победила дружба!"
10 | ON_DRAW_MESSAGE="Игра окончена! Ничья, победила дружба!"
11 | ON_END_TIME="У игрока играющего за {color}ый цвет не осталось времени. Победа игрока играющего за противоположный цвет!"
12 | ON_SOMEONE_MOVE="Сейчас ход другого игрока! Играем в порядке живой очереди!"
13 | ON_RESIGN="Победа игрока за {color}ый цвет, ведь его противник сдался!"
14 | ON_UNSUCCESSFUL_AUTHORIZATION_MESSAGE="Такого кода авторизации не существует!"
15 | ON_ILLEGAL_ACTION_ERROR="Не пытайся сломать систему!"
16 |
17 | PREPARE_TIME=30
18 | GAME_TIME=900
19 | GAMES_AMOUNT=5
20 |
21 | PATH_TO_AVATARS=static/avatars/
22 | URL_TO_PLAYGROUND=/playgrounds/games/{rout}/
23 | BOT_WEBSOCKET=websocket/bot_consumer
24 | SERVER_IP=daphne:8000
25 | PROXY_IP=127.0.0.1
26 | SERVER_PORT=8080
27 | SERVER_HTTP_PROTOCOL=http
28 | PROXY_HTTP_PROTOCOL=http
29 | WEBSOCKET_PROTOCOL=ws
30 | SERVER_AUTHKEY=419a4997-0107-490e-bb83-2538890d8bb7
31 | JWT_LIVETIME=300
--------------------------------------------------------------------------------
/web_django/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/__init__.py
--------------------------------------------------------------------------------
/web_django/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/api/__init__.py
--------------------------------------------------------------------------------
/web_django/api/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/web_django/api/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ApiConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'api'
7 |
--------------------------------------------------------------------------------
/web_django/api/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/api/migrations/__init__.py
--------------------------------------------------------------------------------
/web_django/api/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/web_django/api/responses.py:
--------------------------------------------------------------------------------
1 | class StartGameResponse:
2 | def __init__(self, uuid: str):
3 | self.uuid = uuid
4 |
5 |
6 | class CheckInGameResponse:
7 | def __init__(self, in_game: bool):
8 | self.in_game = in_game
9 |
10 |
11 | class NewAuthorizeTokenResponse:
12 | def __init__(self, success: bool, token: str):
13 | self.success = success
14 | self.token = token
15 |
16 |
17 | class DeleteAuthorizeTokenResponse:
18 | def __init__(self, success):
19 | self.success = success
20 |
21 |
22 | class AuthorizeAttemptResponse:
23 | def __init__(
24 | self,
25 | success: bool,
26 | message: str = "no message"):
27 |
28 | self.success = success
29 | self.message = message
30 |
31 |
32 | class DashboardResponse:
33 | def __init__(self,
34 | user_id, points, username, nickname):
35 | self.user_id = user_id
36 | self.points = points
37 | self.username = username
38 | self.nickname = nickname
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/web_django/api/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework.serializers import Serializer, CharField, BooleanField, FileField, ModelSerializer
2 | from chessboards.models import UserModel
3 |
4 |
5 | class StartGameSerializer(Serializer):
6 | uuid = CharField(max_length=38)
7 |
8 |
9 | class CheckInGameSerializer(Serializer):
10 | in_game = BooleanField()
11 |
12 |
13 | class NewAuthorizeTokenSerializer(Serializer):
14 | success = BooleanField()
15 | token = CharField(max_length=38)
16 |
17 |
18 | class DeleteAuthorizeTokenSerializer(Serializer):
19 | success = BooleanField()
20 |
21 |
22 | class AuthorizeAttemptSerializer(Serializer):
23 | success = BooleanField()
24 |
25 |
26 | class DashboardSerializer(ModelSerializer):
27 | class Meta:
28 | model = UserModel
29 | fields = ["user_id", "points", "username", "nickname"]
30 |
--------------------------------------------------------------------------------
/web_django/api/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/web_django/api/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from api.views import (
4 | start_game,
5 | check_in_game,
6 | new_authorize_token,
7 | authorization_attempt,
8 | check_timer,
9 | download_avatar,
10 | in_database_api,
11 | dashboard,
12 | UserInfo)
13 |
14 | urlpatterns = [
15 | path('check_in_game', check_in_game),
16 | path('new_token', new_authorize_token),
17 | path('authorize', authorization_attempt),
18 | path('start_game', start_game),
19 | path('check_timer', check_timer),
20 | path('add_avatar', download_avatar),
21 | path('in_database', in_database_api),
22 | path('dashboard', dashboard),
23 | path('profile//', UserInfo.as_view())
24 | ]
25 |
--------------------------------------------------------------------------------
/web_django/api/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from rest_framework.response import Response
4 | from rest_framework.request import Request
5 |
6 | from chessboards.models import UserModel
7 |
8 |
9 | async def in_database(user_id: int) -> bool:
10 |
11 | try:
12 | await UserModel.objects.aget(user_id=user_id)
13 | return True
14 |
15 | except UserModel.DoesNotExist:
16 | return False
17 |
18 |
19 | def authorization(func):
20 | async def wrapper(request: Request):
21 | try:
22 | if request.META.get("HTTP_AUTHORIZATION") == os.getenv("SERVER_AUTHKEY"):
23 | return await func(request)
24 |
25 | return Response({'Неверный ключ авторизации!'})
26 |
27 | except KeyError:
28 |
29 | return Response({'Неверный ключ авторизации!'})
30 | return wrapper
31 |
--------------------------------------------------------------------------------
/web_django/api/views.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import os
3 | from asyncio import get_running_loop
4 | from time import time
5 |
6 | from rest_framework.response import Response
7 | from rest_framework.request import Request
8 | from rest_framework.renderers import JSONRenderer
9 | from rest_framework.generics import RetrieveAPIView
10 | from rest_framework.serializers import ModelSerializer
11 | from adrf.decorators import api_view
12 | from api.utils import in_database
13 | from asgiref import sync
14 | import jwt
15 |
16 | from .serializers import (
17 | StartGameSerializer,
18 | CheckInGameSerializer,
19 | NewAuthorizeTokenSerializer,
20 | AuthorizeAttemptSerializer)
21 |
22 | from .responses import (
23 | StartGameResponse,
24 | CheckInGameResponse,
25 | NewAuthorizeTokenResponse,
26 | AuthorizeAttemptResponse)
27 |
28 | from api.utils import authorization
29 |
30 | from chessboards.chess_core.core import get
31 | from chessboards.chess_core.Game import games, Game
32 | from chessboards.models import UserModel
33 |
34 |
35 | @api_view(['POST'])
36 | @authorization
37 | async def start_game(request: Request):
38 | first_user_id, second_user_id = map(int, await sync.sync_to_async(request.data.get)('players'))
39 |
40 | game = Game((first_user_id, second_user_id))
41 |
42 | get_running_loop().create_task(game.start_timers_game())
43 |
44 | return Response(StartGameSerializer(StartGameResponse(game.tag)).data)
45 |
46 |
47 | @api_view(['POST'])
48 | async def check_in_game(request: Request):
49 | user_id = int(request.query_params.get('user_id'))
50 |
51 | user = await get(games, 'players', user_id=user_id)
52 |
53 | if user:
54 | return Response(CheckInGameSerializer(CheckInGameResponse(True)).data)
55 |
56 | return Response(CheckInGameSerializer(CheckInGameResponse(False)).data)
57 |
58 |
59 | @api_view(['POST'])
60 | @authorization
61 | async def new_authorize_token(request: Request):
62 | user_id = await sync.sync_to_async(request.query_params.get)("user_id")
63 | username = await sync.sync_to_async(request.query_params.get)("username")
64 | nickname = await sync.sync_to_async(request.query_params.get)("nickname")
65 |
66 | token = jwt.encode({"user_id": user_id,
67 | "username": username,
68 | "nickname": nickname,
69 | "exp": time() + int(os.getenv("JWT_LIVETIME"))},
70 | os.getenv("SERVER_AUTHKEY"))
71 |
72 | return Response(NewAuthorizeTokenSerializer(NewAuthorizeTokenResponse(True, token)).data)
73 |
74 |
75 | @api_view(['POST'])
76 | async def authorization_attempt(request: Request):
77 | token = request.query_params.get('token')
78 |
79 | decoded_token = jwt.decode(token, os.getenv("SERVER_AUTHKEY"), algorithms=["HS256"])
80 |
81 | if not decoded_token.get("user_id"):
82 | return Response(JSONRenderer().render(AuthorizeAttemptSerializer(AuthorizeAttemptResponse(False, message="Invalid token")).data))
83 |
84 | if decoded_token.get("created_at") + 300 < time():
85 | return Response(JSONRenderer().render(AuthorizeAttemptSerializer(AuthorizeAttemptResponse(False, message="Token expired")).data))
86 |
87 | user_id = decoded_token["user_id"]
88 | username = decoded_token["username"]
89 | nickname = decoded_token["nickname"]
90 |
91 | request.session["user_id"] = user_id
92 |
93 | if not await in_database(user_id):
94 | user = await UserModel.objects.acreate(user_id=user_id, points=0, username=username, nickname=nickname)
95 | await user.asave()
96 |
97 | return Response(JSONRenderer().render(AuthorizeAttemptSerializer(AuthorizeAttemptResponse(True)).data))
98 |
99 |
100 | @api_view(['POST'])
101 | async def check_timer(request: Request):
102 | game_tag = request.query_params.get("tag")
103 |
104 | game: Game = await get(games, '', tag=game_tag)
105 |
106 | if not game:
107 | return Response({"status": 200})
108 |
109 | player = game.get_turn_player()
110 |
111 | if time() - player.timer.last_flip > player.timer.time:
112 | await game.on_end_timer(player)
113 |
114 | return Response({'status': 200})
115 |
116 |
117 | @api_view(['POST'])
118 | @authorization
119 | async def download_avatar(request: Request):
120 | file = request.FILES["file"]
121 | file_name = request.query_params.get("file_name")
122 | with open(os.path.join(os.getenv("PATH_TO_AVATARS"), file_name), "wb") as destination:
123 | for chunk in file.chunks():
124 | destination.write(chunk)
125 |
126 | return Response({"status": 200})
127 |
128 |
129 | @api_view(['GET'])
130 | @authorization
131 | async def in_database_api(request: Request):
132 | user_id = request.query_params.get("user_id")
133 |
134 | if await in_database(user_id):
135 | return Response({"in_database": True})
136 |
137 | return Response({"in_database": False})
138 |
139 |
140 | @api_view(['GET'])
141 | @authorization
142 | async def dashboard(request: Request):
143 |
144 | count = int(request.query_params.get("count"))
145 | if not count:
146 | users = UserModel.objects.all().order_by("-points")
147 | else:
148 | users = UserModel.objects.all().order_by("-points")[:count]
149 |
150 | json = [{"user_id": user.user_id, "points": user.points, "nickname": user.nickname} async for user in users]
151 |
152 | return Response(json)
153 |
154 |
155 | class UserInfo(RetrieveAPIView):
156 | class Serializer(ModelSerializer):
157 | class Meta:
158 | model = UserModel
159 | fields = ["points"]
160 |
161 | queryset = UserModel.objects.all()
162 | serializer_class = Serializer
163 | lookup_field = "user_id"
164 |
--------------------------------------------------------------------------------
/web_django/authorization/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/authorization/__init__.py
--------------------------------------------------------------------------------
/web_django/authorization/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/web_django/authorization/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class AuthorizationConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'authorization'
7 |
--------------------------------------------------------------------------------
/web_django/authorization/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/web_django/authorization/templates/authorization/success_authorization.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 |
4 | {% block css_load %}
5 |
6 | {% load static %}
7 |
8 |
9 | {% endblock %}
10 |
11 | {% block title %}Успешная авторизация {% endblock %}
12 |
13 | {% block body %}
14 |
15 |
16 | {%load static%}
17 |
18 |

19 |
ВЫ АВТОРИЗОВАНЫ. МОЖЕТЕ ЗАКРЫВАТЬ ЭТУ СТРАНИЦУ.
20 |
21 |
22 |
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/web_django/authorization/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/web_django/authorization/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from authorization.views import index
3 |
4 |
5 | urlpatterns = [
6 | path('', index),
7 | ]
--------------------------------------------------------------------------------
/web_django/authorization/views.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from django.shortcuts import render
4 | from asgiref.sync import sync_to_async
5 | import jwt
6 |
7 | from api.utils import in_database
8 | from chessboards.models import UserModel
9 |
10 |
11 | async def index(request):
12 | token = request.GET.get('token')
13 |
14 | try:
15 | decoded_token = jwt.decode(token, os.getenv("SERVER_AUTHKEY"), algorithms=["HS256"])
16 |
17 | except (jwt.ExpiredSignatureError, jwt.DecodeError, jwt.InvalidTokenError):
18 | return render(request, 'error_page/index.html', {"error": "Токен не валидный или истек", "error_code": "401"})
19 |
20 | user_id = decoded_token["user_id"]
21 | username = decoded_token["username"]
22 | nickname = decoded_token["nickname"]
23 |
24 | await sync_to_async(request.session.update)({"user_id": user_id})
25 |
26 | if not await in_database(user_id):
27 | user = await UserModel.objects.acreate(user_id=user_id, points=0, username=username, nickname=nickname)
28 | await user.asave()
29 |
30 | return render(request, "authorization/success_authorization.html")
31 |
--------------------------------------------------------------------------------
/web_django/base/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 | {% load static %}
10 |
11 | {% block css_load %}
12 | {% endblock %}
13 |
14 | {% block title %} {% endblock %}
15 |
16 |
17 |
18 |
19 |
20 | {% block body %}
21 | {% endblock %}
22 |
60 | {% block scripts %}
61 | {% endblock %}
62 |
63 |
--------------------------------------------------------------------------------
/web_django/blacklist/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/blacklist/__init__.py
--------------------------------------------------------------------------------
/web_django/blacklist/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/web_django/blacklist/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class BlacklistConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'blacklist'
7 |
--------------------------------------------------------------------------------
/web_django/blacklist/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from chessboards.models import UserModel
4 |
5 |
6 | class BlacklistModel(models.Model):
7 | blacklist_user_id = models.ForeignKey(UserModel, models.CASCADE, related_name="blacklist_user_id")
8 |
--------------------------------------------------------------------------------
/web_django/blacklist/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/web_django/blacklist/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from blacklist.views import add_to_blacklist, in_blacklist, remove_from_blacklist
4 |
5 |
6 | urlpatterns = [
7 | path("in_blacklist", in_blacklist),
8 | path("add_to_blacklist", add_to_blacklist),
9 | path("remove_from_blacklist", remove_from_blacklist)
10 | ]
--------------------------------------------------------------------------------
/web_django/blacklist/views.py:
--------------------------------------------------------------------------------
1 | from adrf.decorators import api_view
2 | from django.db import models
3 | from rest_framework.request import Request
4 | from rest_framework.response import Response
5 | from asgiref.sync import sync_to_async
6 |
7 | from blacklist.models import BlacklistModel
8 | from chessboards.models import UserModel
9 | from api.utils import authorization
10 |
11 |
12 | @api_view(["GET"])
13 | @authorization
14 | async def in_blacklist(request: Request):
15 | try:
16 | user_id = await (sync_to_async(request.query_params.get)("user_id"))
17 |
18 | await BlacklistModel.objects.aget(blacklist_user_id=await UserModel.objects.aget(user_id=user_id))
19 |
20 | except (UserModel.DoesNotExist, BlacklistModel.DoesNotExist):
21 | return Response({"in_blacklist": True})
22 |
23 |
24 | @api_view(["POST"])
25 | @authorization
26 | async def add_to_blacklist(request: Request):
27 | try:
28 | user_id = await (sync_to_async(request.query_params.get)("user_id"))
29 |
30 | user = BlacklistModel(blacklist_user_id=await UserModel.objects.aget(user_id=user_id))
31 | await user.asave()
32 |
33 | return Response({"status": 200})
34 | except (UserModel.DoesNotExist, BlacklistModel.DoesNotExist):
35 | return Response({"status": 404})
36 |
37 |
38 | @api_view(["POST"])
39 | @authorization
40 | async def remove_from_blacklist(request: Request):
41 | try:
42 | user_id = await (sync_to_async(request.query_params.get)("user_id"))
43 |
44 | user = await BlacklistModel.objects.all().afilter(
45 | blacklist_user_id=await UserModel.objects.aget(user_id=user_id))
46 | await user.adelete()
47 |
48 | return Response({"status": 200})
49 |
50 | except (UserModel.DoesNotExist, BlacklistModel.DoesNotExist):
51 | return Response({"status": 404})
52 |
53 |
--------------------------------------------------------------------------------
/web_django/chessboards/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/chessboards/__init__.py
--------------------------------------------------------------------------------
/web_django/chessboards/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/web_django/chessboards/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ChessboardsConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'chessboards'
7 |
--------------------------------------------------------------------------------
/web_django/chessboards/chess_core/Game.py:
--------------------------------------------------------------------------------
1 | import os
2 | import asyncio
3 | from uuid import uuid4
4 | from time import time
5 | from typing import Optional
6 |
7 | import chess
8 | from channels.consumer import get_channel_layer
9 |
10 | from chessboards.chess_core.User import User
11 | from chessboards.models import GamesModel
12 |
13 |
14 | class Game:
15 | """Класс отвечающий за игру"""
16 |
17 | def __init__(self, users_ids: tuple):
18 | self.board: chess.Board = chess.Board()
19 | self.players: list = []
20 | self.player_1: User = User(users_ids[0], True, self)
21 | self.player_2: User = User(users_ids[1], False, self)
22 | self._started_at = time()
23 | self.tag = str(uuid4())
24 | self.game_end: bool = False
25 |
26 | games.append(self)
27 |
28 | @property
29 | def started_at(self):
30 | return round(self._started_at)
31 |
32 | async def move(self, text_move):
33 | """:raise BaseError если на доске ситуация приводящая к концу игры либо противоречащая её продолжению"""
34 |
35 | turn_player = self.get_turn_player()
36 |
37 | try:
38 |
39 | self.board.push_san(text_move)
40 | except chess.IllegalMoveError as error:
41 | if "missing promotion piece type" in error.args[0]:
42 | self.board.push_san(f"{text_move}q")
43 | else:
44 | raise chess.IllegalMoveError()
45 |
46 | asyncio.get_running_loop().create_task(self.get_turn_player().start_timer(), name=self.tag)
47 |
48 | await turn_player.timer.flip_the_timer()
49 |
50 | await channel_layer.group_send(
51 | self.tag,
52 | {
53 | "type": "update_board",
54 | "board": self.board.board_fen(),
55 | "first_user_time": self.player_1.timer.time,
56 | "second_user_time": self.player_2.timer.time
57 | }
58 | )
59 |
60 | await self.move_checks()
61 |
62 | return self.board.board_fen()
63 |
64 | async def checkmate(self):
65 | """:raise MateError если есть на доске мат"""
66 |
67 | if self.board.is_checkmate():
68 | winner = self.get_winner()
69 | await self.on_win(winner, os.getenv("ON_MATE_MESSAGE").replace('{color}', winner.color_text))
70 |
71 | async def check_stalemate(self):
72 | """:raise DrawError если на доске пат"""
73 |
74 | if self.board.is_stalemate():
75 | await self.on_end_game(os.getenv("ON_STALEMATE_MESSAGE"))
76 |
77 | async def on_check(self):
78 | if self.board.is_check():
79 | await channel_layer.group_send(
80 | self.tag,
81 | {
82 | 'type': 'on_check',
83 | 'recipient': self.get_turn_player().user_id
84 | }
85 | )
86 |
87 | async def move_checks(self):
88 | await self.checkmate()
89 | await self.check_stalemate()
90 | await self.on_check()
91 |
92 | def get_winner(self) -> User:
93 | """:return User - object"""
94 |
95 | return self.player_1 if not self.board.turn else self.player_2
96 |
97 | def get_turn_player(self):
98 | """:return User object"""
99 |
100 | return self.player_1 if self.player_1.color is self.board.turn else self.player_2
101 |
102 | async def start_timers_game(self):
103 | """actions before start game"""
104 |
105 | for player in self.players:
106 | asyncio.get_running_loop().create_task(player.fill_attributes())
107 |
108 | await asyncio.sleep(int(os.getenv("PREPARE_TIME")))
109 |
110 | asyncio.get_running_loop().create_task(self.get_turn_player().start_timer(), name=self.tag)
111 |
112 | async def on_end_game(self, message):
113 | """Коро хендлер срабатывающий после окончания игры"""
114 |
115 | if self.game_end:
116 | return
117 |
118 | self.game_end = True
119 |
120 | self.delete_timers()
121 |
122 | await channel_layer.group_send(
123 | self.tag,
124 | {
125 | 'type': "end_game_event",
126 | 'message': message
127 | })
128 |
129 | games.remove(self)
130 | del self
131 |
132 | def delete_timers(self):
133 | for task in asyncio.all_tasks(asyncio.get_running_loop()):
134 | if task.get_name() == self.tag:
135 | task.cancel()
136 |
137 | async def on_win(self, winner: User, message: str):
138 | await self.on_end_game(message)
139 | await winner.give_points()
140 | await self.add_to_game_registry(winner)
141 |
142 | async def on_draw(self):
143 | await self.add_to_game_registry(None)
144 |
145 | async def add_to_game_registry(self, winner: Optional[User]):
146 |
147 | game = GamesModel(
148 | first_player=self.player_1.model_user,
149 | second_player=self.player_2.model_user,
150 | winner=winner.model_user
151 | )
152 | await game.asave()
153 |
154 | async def on_draw_offer(self):
155 | for player in self.players:
156 | if not player.draw_offer:
157 |
158 | await channel_layer.group_send(
159 | self.tag,
160 | {
161 | 'type': "draw_offer_event",
162 | 'recipient': player.user_id
163 | }
164 | )
165 | return
166 |
167 | await self.on_end_game(os.getenv("ON_DRAW_MESSAGE"))
168 |
169 | async def on_give_up(self, user_id: int):
170 | for player in self.players:
171 | if player.user_id != user_id:
172 | await self.on_win(player, os.getenv("ON_RESIGN").replace('{color}', player.color_text))
173 |
174 | async def on_end_timer(self, loser: User):
175 |
176 | if self.game_end:
177 | return
178 |
179 | # noinspection PyTypeChecker
180 |
181 | await self.on_win((lambda player: self.player_1 if player == self.player_2 else self.player_2)(loser), os.getenv("ON_END_TIME").replace('{color}', loser.color_text))
182 |
183 | def get_legal_moves(self, cell: str) -> list:
184 |
185 | """:return legal moves for piece on cell"""
186 |
187 | return [str(move)[2:] for move in self.board.legal_moves if str(move)[:2] == cell]
188 |
189 |
190 | channel_layer = get_channel_layer("default")
191 | games = []
192 |
--------------------------------------------------------------------------------
/web_django/chessboards/chess_core/Timer.py:
--------------------------------------------------------------------------------
1 | import os
2 | from typing import Optional
3 | from time import time as _time
4 | import asyncio
5 |
6 |
7 | class Timer:
8 | """class Timer"""
9 |
10 | def __init__(
11 | self,
12 | own_object,
13 | time: int = int(os.getenv("GAME_TIME")),
14 | ):
15 | self.__event = asyncio.Event()
16 |
17 | self.last_flip = None
18 | self.own_object = own_object
19 | self._time = time
20 |
21 | @property
22 | def time(self):
23 | return round(self._time)
24 |
25 | async def __wait_move(self):
26 | """wait move"""
27 |
28 | self.last_flip = _time()
29 |
30 | waiter_task = asyncio.get_running_loop().create_task(self.__event.wait())
31 | await waiter_task
32 |
33 | self.__event.clear()
34 |
35 | async def start_timer(self):
36 | """Start the timer"""
37 |
38 | loop = asyncio.get_running_loop()
39 |
40 | try:
41 | await Timer.wait_for(self.__wait_move(), timeout=self._time, loop=loop)
42 | except asyncio.TimeoutError:
43 | loop.create_task(self.own_object.own_object.on_end_timer(self.own_object))
44 |
45 | async def update_timer(self):
46 |
47 | if _time() - self.own_object.own_object.started_at < int(os.getenv("PREPARE_TIME")):
48 | return
49 |
50 | if not self.last_flip:
51 | self.last_flip = _time()
52 |
53 | self._time = self._time - (_time() - self.last_flip)
54 |
55 | if self._time <= 0:
56 | await self.own_object.own_object.on_end_timer(self.own_object)
57 |
58 | self.last_flip = _time()
59 |
60 | async def flip_the_timer(self):
61 | """Меняет положение таймера (активный/деактивный)"""
62 |
63 | self.__event.set()
64 |
65 | await self.update_timer()
66 |
67 | return self._time
68 |
69 | @staticmethod
70 | async def wait_for(task, timeout=0, loop: Optional[asyncio.AbstractEventLoop] = None):
71 |
72 | task = loop.create_task(task)
73 |
74 | await asyncio.sleep(timeout)
75 |
76 | if not task.done():
77 | raise asyncio.TimeoutError()
78 |
--------------------------------------------------------------------------------
/web_django/chessboards/chess_core/User.py:
--------------------------------------------------------------------------------
1 | import os
2 | from typing import Optional
3 | import glob
4 |
5 | from chessboards.chess_core.Timer import Timer
6 | from chessboards.models import UserModel
7 |
8 |
9 | class User:
10 | """User class"""
11 |
12 | def __init__(self, user_id: int, color: bool, own_object):
13 | self.user_id = int(user_id)
14 | self.color: bool = color
15 | self.timer: Timer = Timer(self)
16 | self.color_text = (lambda x: "бел" if x else "чёрн")(self.color)
17 | self.draw_offer = False
18 | self.own_object = own_object
19 | self.avatar_path = None
20 | self.model_user = None
21 |
22 | self.points: Optional[int] = None
23 | self.nickname: Optional[str] = None
24 | self.username: Optional[str] = None
25 | self.session_id: Optional[str] = None
26 | self.avatar_path: Optional[str] = None
27 |
28 | self.own_object.players.append(self)
29 |
30 | async def fill_attributes(self):
31 | """fill attributes from database"""
32 | self.model_user = await UserModel.objects.aget(user_id=self.user_id)
33 |
34 | self.points = self.model_user.points
35 | self.nickname = self.model_user.nickname
36 | self.username = self.model_user.username
37 |
38 | file_name = glob.glob(f"{os.getenv('PATH_TO_AVATARS')}{self.user_id}.*")
39 |
40 | if len(file_name):
41 | self.avatar_path = file_name[0][file_name[0].index('avatars/'):]
42 | return
43 |
44 | self.avatar_path = "images/unknown_user.png"
45 |
46 | async def move(self, start_cell: str, end_cell: str):
47 | """move in board and flip timer"""
48 |
49 | await self.timer.flip_the_timer()
50 | await self.own_object.move("".join([start_cell, end_cell]))
51 |
52 | async def draw(self):
53 | self.draw_offer = True
54 | await self.own_object.on_draw_offer()
55 |
56 | async def give_up(self):
57 | await self.own_object.on_give_up(self.user_id)
58 |
59 | async def start_timer(self):
60 | """timer starter"""
61 |
62 | await self.timer.start_timer()
63 |
64 | async def give_points(self):
65 | """add points count after end game"""
66 |
67 | self.model_user.points += 1
68 | await self.model_user.asave()
69 |
--------------------------------------------------------------------------------
/web_django/chessboards/chess_core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/chessboards/chess_core/__init__.py
--------------------------------------------------------------------------------
/web_django/chessboards/chess_core/core.py:
--------------------------------------------------------------------------------
1 | from asgiref.sync import sync_to_async
2 |
3 |
4 | @sync_to_async()
5 | def get(from_list: list, path: str, **kwargs):
6 | """The method allows you to get an object according to the condition set using **kwargs and path
7 |
8 | :arg: from_list - The list in which the search is performed
9 | :arg: path - path the path from the class attributes to the class to get (str)
10 | :arg: **kwarg - Give one argument with the name corresponding to the argument by which the comparison will be made, and put the value itself that you are looking for in the value
11 |
12 | :return: class which you want get
13 |
14 | Ex.:
15 |
16 | print(get(games, "players.timer", time=30))
17 | # Output: Timer object
18 |
19 | (#GOVNOCODE #YANDERECODING #SHITPOST)
20 |
21 | """
22 |
23 | positions = path.split('.')
24 | value_name = list(kwargs.keys())[0]
25 |
26 | if path == '':
27 | positions = []
28 |
29 | for obj in from_list:
30 |
31 | for position in positions:
32 |
33 | try:
34 | obj = obj.__dict__[position]
35 | except TypeError:
36 | for i in obj:
37 | obj = i.__dict__[position]
38 | if obj.__dict__[value_name] == kwargs[value_name]:
39 | return obj
40 |
41 | try:
42 | if obj.__dict__[value_name] == kwargs[value_name]:
43 | return obj
44 | except AttributeError:
45 | for i in obj:
46 | if i.__dict__[value_name] == kwargs[value_name]:
47 | return i
48 |
--------------------------------------------------------------------------------
/web_django/chessboards/consumers.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from channels.generic.websocket import AsyncJsonWebsocketConsumer
4 | from chess import IllegalMoveError
5 | from autobahn.exception import Disconnected
6 | from asgiref.sync import sync_to_async
7 |
8 | from chessboards.chess_core.core import get
9 | from chessboards.chess_core.Game import games
10 | from django_log.log import log
11 |
12 |
13 | class UserWebsocket(AsyncJsonWebsocketConsumer):
14 |
15 | async def connect(self):
16 |
17 | self.board_tag = self.scope['url_route']['kwargs']['tag']
18 | self.user = await get(
19 | games,
20 | 'players',
21 | user_id=await sync_to_async(int)(await sync_to_async(self.scope["session"].get)("user_id")))
22 |
23 | await self.channel_layer.group_add(
24 | self.board_tag,
25 | self.channel_name
26 | )
27 |
28 | await self.accept()
29 |
30 | async def disconnect(self, code):
31 | await self.channel_layer.group_discard(
32 | self.board_tag,
33 | self.channel_name
34 | )
35 |
36 | await self.close()
37 |
38 | async def receive_json(self, content, **kwargs):
39 |
40 | # switch case construction does not exit in python 3.9 so:
41 |
42 | if content["type"] == "draw_offer":
43 | await self.draw_offer()
44 | elif content["type"] == "give_up":
45 | await self.give_up()
46 | elif content["type"] == "move":
47 | await self.move(content["start_cell"], content["end_cell"])
48 | elif content["type"] == "get_legal_moves":
49 | await self.get_legal_moves(content["figure_cell"])
50 |
51 | async def send_json(self, content, close=False):
52 | try:
53 | await super().send_json(content, close=close)
54 | except Disconnected:
55 | await sync_to_async(log.info)("autobahn.exception.Disconnection error. ")
56 |
57 | async def end_game_event(self, event):
58 | await self.send_json({"event": 'end_game', "message": event['message']})
59 | await self.disconnect(200)
60 |
61 | async def draw_offer_event(self, event):
62 | await self.send_json({"event": "draw_offer", "recipient": event['recipient']})
63 |
64 | async def on_check(self, event):
65 | await self.send_json({"event": "on_check", "recipient": event['recipient']})
66 |
67 | async def update_board(self, event):
68 | await self.send_json({
69 | "event": "update_board",
70 | "board": event["board"],
71 | "first_user_time": round(event["first_user_time"]),
72 | "second_user_time": round(event["second_user_time"])})
73 |
74 | async def illegal_move_error(self, message):
75 | await self.send_json({"event": "illegal_move_error", "message": message})
76 |
77 | async def error(self, message):
78 | await self.send_json({"event": "error", "message": message})
79 |
80 | async def move(self, start_cell: str, end_cell: str):
81 |
82 | if not self.user:
83 | return
84 |
85 | if self.user.own_object.board.turn is not self.user.color:
86 | await self.error(await (sync_to_async(os.getenv)("ON_ILLEGAL_ACTION_ERROR")))
87 | return
88 |
89 | try:
90 | await self.user.move(start_cell, end_cell)
91 | except IllegalMoveError:
92 | await self.illegal_move_error(await (sync_to_async(os.getenv)("ILLEGAL_MOVE_ERROR")))
93 |
94 | async def get_legal_moves(self, figure_cell: str):
95 |
96 | if not self.user:
97 | return
98 |
99 | cells = await sync_to_async(self.user.own_object.get_legal_moves)(figure_cell)
100 |
101 | await self.send_json({"event": "legal_moves", "cells": cells})
102 |
103 | async def draw_offer(self):
104 |
105 | if not self.user:
106 | return
107 |
108 | await self.user.draw()
109 |
110 | async def give_up(self):
111 |
112 | if not self.user:
113 | return
114 |
115 | await self.user.give_up()
116 |
--------------------------------------------------------------------------------
/web_django/chessboards/games_management.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web_django/chessboards/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/chessboards/migrations/__init__.py
--------------------------------------------------------------------------------
/web_django/chessboards/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class UserModel(models.Model):
5 |
6 | user_id = models.BigIntegerField(primary_key=True)
7 | points = models.IntegerField()
8 | nickname = models.TextField()
9 | username = models.TextField()
10 |
11 | class Meta:
12 | db_table = "users"
13 |
14 |
15 | class GamesModel(models.Model):
16 | first_player = models.ForeignKey(UserModel, models.CASCADE, related_name="first_player_id")
17 | second_player = models.ForeignKey(UserModel, models.CASCADE, related_name="second_player_id")
18 | winner = models.ForeignKey(UserModel, models.CASCADE, related_name="winner_id")
19 |
--------------------------------------------------------------------------------
/web_django/chessboards/routing.py:
--------------------------------------------------------------------------------
1 | from chessboards.consumers import UserWebsocket
2 | from django.urls import path, re_path, utils
3 |
4 |
5 | websocket_urlpatterns = [
6 | path('websocket/games//', UserWebsocket.as_asgi())
7 | ]
8 |
--------------------------------------------------------------------------------
/web_django/chessboards/templates/chessboards/game.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title%} App | Chess {% endblock %}
4 |
5 | {% block css_load %}
6 |
7 | {% load static %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {% endblock %}
26 |
27 | {% block body %}
28 |
29 |
30 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
Игра ещё не началась, подождите ещё
56 | 30
57 |
58 |
59 |
60 |
61 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
После нажатия на кнопку игра заканчивается, а ты автоматически считаешься проигравшим!
111 |
115 |
116 |
117 |
118 |
ТЫ ОТПРАВИЛ ЗАПРОС НА НИЧЬЮ.
119 |
Если противник согласится на неё, игра закончится, а победит дружба.
120 |
123 |
124 |
125 |
126 |
ПРОТИВНИК ПРЕДЛАГАЕТ НИЧЬЮ
127 |
Твой противник предлагает ничью. Если ты нажмешь на кнопку принять, игра закончится, а победит дружба
128 |
132 |
133 |
134 |
135 |
ИГРА ОКОНЧЕНА
136 |
message
137 |
140 |
141 |
142 |
143 |
НЕВОЗМОЖНЫЙ ХОД
144 |
145 |
148 |
149 |
150 |
151 |
УГРОЗА КОРОЛЮ
152 |
Твой противник объявил твоему королю шах! Будь внимателен!
153 |
156 |
157 |
158 |
159 |
ПОДТВЕРДИТЕ ПЕРЕХОД
160 |
message
161 |
165 |
166 |
167 | {% load static %}
168 | {% static 'images/1x1.png' %}
169 | {% static 'move_sound.mp3' %}
170 |
171 | {% endblock %}
172 |
173 | {% block scripts %}
174 |
175 |
178 |
179 | {% load static %}
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
200 |
201 | {% load static %}
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
221 |
222 | {% endblock %}
223 |
--------------------------------------------------------------------------------
/web_django/chessboards/templates/chessboards/game_spectator.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title%} App | Chess {% endblock %}
4 |
5 | {% block css_load %}
6 |
7 | {% load static %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {% endblock %}
26 |
27 | {% block body %}
28 |
29 |
30 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
48 |
61 |
62 |
63 |
64 |
65 |
Игра ещё не началась, подождите ещё
66 | 30
67 |
68 |
69 |
70 |
71 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
ИГРА ОКОНЧЕНА
120 |
message
121 |
124 |
125 |
126 |
127 |
ПОДТВЕРДИТЕ ПЕРЕХОД
128 |
message
129 |
133 |
134 |
135 | {% load static %}
136 | {% static 'images/1x1.png' %}
137 |
138 | {% endblock %}
139 |
140 | {% block scripts %}
141 |
142 |
145 |
146 | {% load static %}
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
164 |
165 | {% load static %}
166 |
167 |
168 |
169 |
170 |
171 |
172 |
181 |
182 | {% endblock %}
183 |
--------------------------------------------------------------------------------
/web_django/chessboards/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/web_django/chessboards/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from chessboards.views import game_view
3 |
4 | urlpatterns = [
5 | path('games//', game_view, name="chess_game")
6 | ]
7 |
--------------------------------------------------------------------------------
/web_django/chessboards/views.py:
--------------------------------------------------------------------------------
1 | import os
2 | from time import time
3 |
4 | from django.http import HttpRequest
5 | from django.shortcuts import render
6 | from asgiref.sync import sync_to_async
7 |
8 | from chessboards.chess_core.core import get
9 | from chessboards.chess_core.Game import games
10 |
11 |
12 | async def game_view(request: HttpRequest, **kwargs):
13 |
14 | game_object = await get(games, '', tag=kwargs['tag'])
15 | user_id = await sync_to_async(request.session.get)("user_id")
16 |
17 | if not game_object or not user_id:
18 | return await sync_to_async(render)(request, 'error_page/index.html', {'error_code': '404', "error": "Игры с таким uuid не существует"})
19 |
20 | user_id = int(user_id)
21 |
22 | user = await get(game_object.players, '', user_id=user_id)
23 |
24 | kwargs["game"] = game_object
25 | kwargs["user"] = user
26 |
27 | await game_object.get_turn_player().timer.update_timer()
28 |
29 | if user:
30 | return await game_player_mode(request, **kwargs)
31 |
32 | return await game_spectator_mode(request, **kwargs)
33 |
34 |
35 | @sync_to_async()
36 | def game_player_mode(request: HttpRequest, **kwargs):
37 |
38 | game = kwargs["game"]
39 | user = kwargs["user"]
40 |
41 | return render(request, 'chessboards/game.html', {
42 | 'prepare_time': int(os.getenv("PREPARE_TIME")) - round(time() - game.started_at),
43 | 'board_tag': kwargs['tag'],
44 | 'user_id': user.user_id,
45 | 'board_fen': game.board.board_fen(),
46 | 'first_player_nickname': game.player_1.nickname,
47 | 'second_player_nickname': game.player_2.nickname,
48 | 'first_player_avatar': game.player_1.avatar_path,
49 | 'second_player_avatar': game.player_2.avatar_path,
50 | 'first_player_time': game.player_1.timer.time,
51 | 'second_player_time': game.player_2.timer.time,
52 | 'draw_offer': user.draw_offer,
53 | 'color': user.color,
54 | 'turn': game.board.turn})
55 |
56 |
57 | @sync_to_async()
58 | def game_spectator_mode(request: HttpRequest, **kwargs):
59 |
60 | game = kwargs["game"]
61 |
62 | return render(request, 'chessboards/game_spectator.html', {
63 | 'prepare_time': int(os.getenv('PREPARE_TIME')) - round(time() - game.started_at),
64 | 'board_tag': kwargs['tag'],
65 | 'board_fen': game.board.board_fen(),
66 | 'first_player_nickname': game.player_1.nickname,
67 | 'second_player_nickname': game.player_2.nickname,
68 | 'first_player_avatar': game.player_1.avatar_path,
69 | 'second_player_avatar': game.player_2.avatar_path,
70 | 'first_player_time': game.player_1.timer.time,
71 | 'second_player_time': game.player_2.timer.time,
72 | 'turn': game.board.turn})
73 |
--------------------------------------------------------------------------------
/web_django/django_log/log.py:
--------------------------------------------------------------------------------
1 | import os
2 | from logging import getLogger, FileHandler, Formatter, basicConfig
3 |
4 | basicConfig(level='DEBUG')
5 |
6 | log_handler = FileHandler(os.path.join(os.path.dirname(__file__), 'main.log'), 'a+')
7 | log_handler.setFormatter(Formatter("%(name)s %(asctime)s %(levelname)s %(message)s %(exc_info)s %(lineno)s"))
8 |
9 | log = getLogger('django_log')
10 | log.addHandler(log_handler)
11 | log.setLevel('DEBUG')
12 |
13 | asyncio_log = getLogger('asyncio')
14 | asyncio_log.setLevel('DEBUG')
15 | asyncio_log.addHandler(log_handler)
16 |
17 | log.info('logging successful started')
18 |
--------------------------------------------------------------------------------
/web_django/error_page/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/error_page/__init__.py
--------------------------------------------------------------------------------
/web_django/error_page/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/web_django/error_page/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ErrorPageConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'error_page'
7 |
--------------------------------------------------------------------------------
/web_django/error_page/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Justiks1337/ChessBot/930b08e5f11bb77708c9fb99ff735816c84635ae/web_django/error_page/migrations/__init__.py
--------------------------------------------------------------------------------
/web_django/error_page/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/web_django/error_page/templates/error_page/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block css_load %}
4 |
5 | {% load static %}
6 |
7 |
8 | {% endblock%}
9 |
10 | {% block title %} Ошибка {% endblock %}
11 |
12 | {% block body %}
13 |
19 |
20 | {% endblock %}
--------------------------------------------------------------------------------
/web_django/error_page/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/web_django/error_page/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.http import HttpRequest
3 |
4 |
5 | def handler404(request: HttpRequest, *args, **kwargs):
6 | return render(request, 'error_page/index.html', {'error_code': "404", "error": "СТРАНИЦА НЕ НАЙДЕНА"})
7 |
8 |
--------------------------------------------------------------------------------
/web_django/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web_django.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
24 |
--------------------------------------------------------------------------------
/web_django/requirements.txt:
--------------------------------------------------------------------------------
1 | adrf==0.1.9
2 | asgiref==3.8.1
3 | async-property==0.2.2
4 | attrs==25.3.0
5 | autobahn==24.4.2
6 | Automat==25.4.16
7 | certifi==2025.4.26
8 | cffi==1.17.1
9 | channels==4.2.2
10 | charset-normalizer==3.4.2
11 | chess==1.11.2
12 | constantly==23.10.4
13 | cryptography==45.0.3
14 | daphne==4.2.0
15 | Django==5.2.1
16 | django-channels==0.7.0
17 | django-cors-headers==4.7.0
18 | django-ipware==7.0.1
19 | djangorestframework==3.16.0
20 | hyperlink==21.0.0
21 | idna==3.10
22 | incremental==24.7.2
23 | oauthlib==3.2.2
24 | psycopg2-binary==2.9.10
25 | pyasn1==0.6.1
26 | pyasn1_modules==0.4.2
27 | pycparser==2.22
28 | PyJWT==2.10.1
29 | pyOpenSSL==25.1.0
30 | python-dotenv==1.1.0
31 | python-ipware==3.0.0
32 | requests==2.32.3
33 | requests-oauthlib==2.0.0
34 | service-identity==24.2.0
35 | setuptools==80.8.0
36 | six==1.17.0
37 | sqlparse==0.5.3
38 | Twisted==24.11.0
39 | txaio==23.1.1
40 | typing_extensions==4.13.2
41 | urllib3==2.4.0
42 | zope.interface==7.2
43 |
--------------------------------------------------------------------------------
/web_django/web_django/__init__.py:
--------------------------------------------------------------------------------
1 | from asyncio import get_event_loop
2 |
3 |
4 | main_loop = get_event_loop()
5 |
--------------------------------------------------------------------------------
/web_django/web_django/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for web_django project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django import setup
13 | from django.core.management import call_command
14 | from django.core.asgi import get_asgi_application
15 | from channels.routing import ProtocolTypeRouter, URLRouter
16 | from channels.security.websocket import AllowedHostsOriginValidator
17 | from channels.sessions import SessionMiddlewareStack
18 |
19 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web_django.settings')
20 | setup()
21 |
22 |
23 | asgi_application = get_asgi_application()
24 |
25 | from chessboards.routing import websocket_urlpatterns as chessboards_websocket_url_patterns
26 | from django_log.log import log
27 |
28 | os.environ.setdefault('PYTHONASYNCIODEBUG', '1')
29 | os.environ.setdefault('SERVER_GATEWAY_INTERFACE', 'ASGI')
30 |
31 | call_command('makemigrations')
32 | call_command('migrate')
33 |
34 | log.info(f'server successful started. Server gateway interface: ASGI')
35 |
36 | application = ProtocolTypeRouter({
37 | "http": asgi_application,
38 | "websocket": AllowedHostsOriginValidator(
39 | SessionMiddlewareStack(URLRouter(chessboards_websocket_url_patterns)))
40 | })
41 |
--------------------------------------------------------------------------------
/web_django/web_django/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for web_django project.
3 |
4 | Generated by 'django-admin startproject' using Django 4.2.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/4.2/ref/settings/
11 | """
12 |
13 | import os
14 | from pathlib import Path
15 | import dotenv
16 |
17 |
18 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
19 | BASE_DIR = Path(__file__).resolve().parent.parent
20 |
21 | # Read environment variables
22 | dotenv.load_dotenv(os.path.join(BASE_DIR, ".env"))
23 |
24 | # Quick-start development settings - unsuitable for production
25 | # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
26 |
27 | # SECURITY WARNING: keep the secret key used in production secret!
28 | SECRET_KEY = os.getenv("SERVER_AUTHKEY")
29 |
30 | # SECURITY WARNING: don't run with debug turned on in production!
31 | DEBUG = False
32 |
33 | ALLOWED_HOSTS = ["127.0.0.1",
34 | "192.168.1.60",
35 | "45.142.122.143",
36 | "chess.kazoe.ru",
37 | "chess-kb.ru",
38 | "beta.chess-kb.ru",
39 | "daphne"]
40 |
41 |
42 | # Application definition
43 | INSTALLED_APPS = [
44 | 'django.contrib.admin',
45 | 'django.contrib.auth',
46 | 'django.contrib.contenttypes',
47 | 'django.contrib.sessions',
48 | 'django.db.models',
49 | 'django.contrib.messages',
50 | 'daphne',
51 | 'django.contrib.staticfiles',
52 | 'rest_framework',
53 | 'corsheaders',
54 | 'adrf',
55 | 'channels',
56 | 'chessboards',
57 | 'blacklist'
58 | ]
59 |
60 | MIDDLEWARE = [
61 | 'django.middleware.security.SecurityMiddleware',
62 | 'django.contrib.sessions.middleware.SessionMiddleware',
63 | 'django.middleware.common.CommonMiddleware',
64 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
65 | 'django.contrib.messages.middleware.MessageMiddleware',
66 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
67 | 'django.middleware.common.CommonMiddleware'
68 | ]
69 |
70 | CORS_ORIGIN_ALLOW_ALL = True
71 |
72 | ROOT_URLCONF = 'web_django.urls'
73 |
74 | CHANNEL_LAYERS = {
75 | "default": {
76 | "BACKEND": "channels.layers.InMemoryChannelLayer"
77 | }
78 | }
79 |
80 | TEMPLATES = [
81 | {
82 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
83 | 'DIRS': [
84 | 'authorization/templates',
85 | 'error_page/templates',
86 | 'chessboards/templates',
87 | 'base'
88 | ],
89 | 'APP_DIRS': True,
90 | 'OPTIONS': {
91 | 'context_processors': [
92 | 'django.template.context_processors.debug',
93 | 'django.template.context_processors.request',
94 | 'django.contrib.auth.context_processors.auth',
95 | 'django.contrib.messages.context_processors.messages',
96 | ],
97 | },
98 | },
99 | ]
100 |
101 | ASGI_APPLICATION = 'asgi.application'
102 |
103 |
104 | # Database
105 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases
106 |
107 | DATABASES = {
108 | 'default': {
109 | 'ENGINE': 'django.db.backends.postgresql',
110 | 'NAME': os.getenv("DATABASE_NAME"),
111 | 'USER': os.getenv("DATABASE_USER"),
112 | 'PASSWORD': os.getenv("DATABASE_PASSWORD"),
113 | 'HOST': os.getenv("DATABASE_HOST"),
114 | 'PORT': int(os.getenv("DATABASE_PORT"))
115 | }
116 | }
117 |
118 |
119 | # Password validation
120 | # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
121 |
122 | AUTH_PASSWORD_VALIDATORS = [
123 | {
124 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
125 | },
126 | {
127 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
128 | },
129 | {
130 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
131 | },
132 | {
133 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
134 | },
135 | ]
136 |
137 | # Internationalization
138 | # https://docs.djangoproject.com/en/4.2/topics/i18n/
139 |
140 | LANGUAGE_CODE = 'en-us'
141 |
142 | TIME_ZONE = 'UTC'
143 |
144 | USE_I18N = True
145 |
146 | USE_TZ = True
147 |
148 |
149 | # Static files (CSS, JavaScript, Images)
150 | # https://docs.djangoproject.com/en/4.2/howto/static-files/
151 |
152 | STATIC_URL = '/static/'
153 |
154 | STATICFILES_DIRS = [
155 | BASE_DIR / "static",
156 | ]
157 |
158 | # Default primary key field type
159 | # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
160 |
161 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
162 |
163 | X_FRAME_OPTIONS = 'SAMEORIGIN'
164 |
165 | LOGGING = {
166 | 'version': 1,
167 | 'disable_existing_loggers': False,
168 | 'handlers': {
169 | 'file': {
170 | 'level': 'DEBUG',
171 | 'class': 'logging.FileHandler',
172 | 'filename': 'django_log/main.log',
173 | },
174 | },
175 | 'loggers': {
176 | 'django': {
177 | 'handlers': ['file'],
178 | 'level': 'DEBUG',
179 | 'propagate': True,
180 | },
181 | },
182 | }
183 |
184 |
185 | SECURE_CROSS_ORIGIN_OPENER_POLICY = None
186 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
187 |
--------------------------------------------------------------------------------
/web_django/web_django/urls.py:
--------------------------------------------------------------------------------
1 | """
2 | URL configuration for web_django project.
3 |
4 | The `urlpatterns` list routes URLs to views. For more information please see:
5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/
6 | Examples:
7 | Function views
8 | 1. Add an import: from my_app import views
9 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
10 | Class-based views
11 | 1. Add an import: from other_app.views import Home
12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
13 | Including another URLconf
14 | 1. Import the include() function: from django.urls import include, path
15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16 | """
17 | from django.contrib import admin
18 | from django.urls import path, include
19 | from django.conf import settings
20 | from django.conf.urls.static import static
21 | from error_page.views import handler404
22 |
23 | urlpatterns = [
24 | path('', include('authorization.urls')),
25 | path('admin/', admin.site.urls),
26 | path('playgrounds/', include('chessboards.urls')),
27 | path('api/v1/', include('api.urls')),
28 | path('authorization/', include('authorization.urls')),
29 | path('blacklist/', include('blacklist.urls'))
30 | ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
31 |
32 | handler404 = handler404
33 |
--------------------------------------------------------------------------------
/web_django/web_django/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for web_django project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
13 | os.environ.setdefault('SERVER_GATEWAY_INTERFACE', 'WSGI')
14 |
15 |
--------------------------------------------------------------------------------