diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..15f45d7
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,84 @@
+name: CI
+
+on:
+ - push
+ - pull_request
+
+jobs:
+ Build:
+ runs-on: ubuntu-20.04
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.9
+
+ - name: Install pipenv
+ run: pip install pipenv
+
+ - id: cache-pipenv
+ name: Download cache
+ uses: actions/cache@v2
+ with:
+ path: ~/.local/share/virtualenvs
+ key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }}
+
+ - name: Install dependencies
+ if: steps.cache-pipenv.outputs.cache-hit != 'true'
+ run: pipenv install --deploy --dev
+
+ - name: Build gettext strings
+ run: pipenv run sphinx-build -b gettext source build/locale
+
+ - name: Push and Pull strings from the Crowdin
+ uses: crowdin/github-action@1.1.0
+ # TODO: remove upgrade branch after finishing upgrading process
+ if: contains(fromJson('["refs/heads/master", "refs/heads/upgrade"]'), github.ref)
+ with:
+ token: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
+
+ upload_sources: true
+
+ download_translations: true
+ push_translations: false
+ download_language: en # Temporary limit only to English
+
+ - name: Fix permissions to the locale dir
+ run: sudo chown -R $USER:$USER locale
+
+ - name: Build docs
+ run: pipenv run python build-multilang.py
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v2
+ with:
+ name: build
+ path: build
+ retention-days: 7
+
+ Deploy:
+ runs-on: ubuntu-latest
+ needs: Build
+ # TODO: remove upgrade branch after finishing upgrading process
+ if: contains(fromJson('["refs/heads/master", "refs/heads/upgrade"]'), github.ref)
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - id: download
+ name: Download build artifacts
+ uses: actions/download-artifact@v2
+ with:
+ name: build
+
+ - name: Deploy to the GitHub Pages
+ uses: JamesIves/github-pages-deploy-action@4.1.0
+ with:
+ branch: gh-pages
+ folder: ${{ steps.download.outputs.download-path }}
+ single-commit: true
diff --git a/.gitignore b/.gitignore
index b24904a..e08409d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,6 @@
### venv folder
/venv
+
+### The directory for the locales from the Crowdin
+/locale
diff --git a/.nojekyll b/.nojekyll
new file mode 100644
index 0000000..e69de29
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 260e264..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-os: linux
-dist: xenial
-language: python
-
-cache: pip
-
-install:
- - pip install -r requirements.txt
-
-script:
- - sphinx-build source build
-
-deploy:
- provider: pages
- strategy: git
- skip_cleanup: true
- local_dir: ./build
- token: $GITHUB_TOKEN
- on:
- branch: master
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000..3adf7cf
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,16 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+Sphinx = ">=3.0,<4.0"
+sphinx-rtd-theme = "~=0.5.1"
+
+[dev-packages]
+lxml = ">=4.6.2,<5.0"
+sphinx-autobuild = ">=2020.09.01"
+sphinx-intl = ">=2.0,<3.0"
+
+[requires]
+python_version = "3.9"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 0000000..c4fa440
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,626 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "f276bc0ed2af04e02fbf86b128186f6c4632d643f77c387892d9c775dbfb068f"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "3.9"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "alabaster": {
+ "hashes": [
+ "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
+ "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
+ ],
+ "version": "==0.7.12"
+ },
+ "babel": {
+ "hashes": [
+ "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5",
+ "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.9.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
+ "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
+ ],
+ "version": "==2020.12.5"
+ },
+ "chardet": {
+ "hashes": [
+ "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
+ "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==4.0.0"
+ },
+ "docutils": {
+ "hashes": [
+ "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
+ "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==0.16"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+ "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.10"
+ },
+ "imagesize": {
+ "hashes": [
+ "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
+ "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.2.0"
+ },
+ "jinja2": {
+ "hashes": [
+ "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
+ "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.11.3"
+ },
+ "markupsafe": {
+ "hashes": [
+ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
+ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
+ "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
+ "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
+ "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
+ "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f",
+ "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39",
+ "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
+ "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
+ "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014",
+ "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f",
+ "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
+ "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
+ "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
+ "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
+ "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
+ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
+ "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
+ "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
+ "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85",
+ "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1",
+ "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
+ "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
+ "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
+ "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850",
+ "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0",
+ "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
+ "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
+ "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb",
+ "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
+ "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
+ "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
+ "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1",
+ "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2",
+ "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
+ "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
+ "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
+ "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
+ "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
+ "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
+ "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
+ "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
+ "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
+ "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
+ "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
+ "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
+ "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
+ "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
+ "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
+ "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
+ "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
+ "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.1.1"
+ },
+ "packaging": {
+ "hashes": [
+ "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
+ "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.9"
+ },
+ "pygments": {
+ "hashes": [
+ "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94",
+ "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==2.8.1"
+ },
+ "pyparsing": {
+ "hashes": [
+ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
+ "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
+ ],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.4.7"
+ },
+ "pytz": {
+ "hashes": [
+ "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
+ "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
+ ],
+ "version": "==2021.1"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
+ "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.25.1"
+ },
+ "snowballstemmer": {
+ "hashes": [
+ "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2",
+ "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"
+ ],
+ "version": "==2.1.0"
+ },
+ "sphinx": {
+ "hashes": [
+ "sha256:672cfcc24b6b69235c97c750cb190a44ecd72696b4452acaf75c2d9cc78ca5ff",
+ "sha256:ef64a814576f46ec7de06adf11b433a0d6049be007fefe7fd0d183d28b581fac"
+ ],
+ "index": "pypi",
+ "version": "==3.5.2"
+ },
+ "sphinx-rtd-theme": {
+ "hashes": [
+ "sha256:eda689eda0c7301a80cf122dad28b1861e5605cbf455558f3775e1e8200e83a5",
+ "sha256:fa6bebd5ab9a73da8e102509a86f3fcc36dec04a0b52ea80e5a033b2aba00113"
+ ],
+ "index": "pypi",
+ "version": "==0.5.1"
+ },
+ "sphinxcontrib-applehelp": {
+ "hashes": [
+ "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
+ "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.2"
+ },
+ "sphinxcontrib-devhelp": {
+ "hashes": [
+ "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
+ "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.2"
+ },
+ "sphinxcontrib-htmlhelp": {
+ "hashes": [
+ "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
+ "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.3"
+ },
+ "sphinxcontrib-jsmath": {
+ "hashes": [
+ "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
+ "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.1"
+ },
+ "sphinxcontrib-qthelp": {
+ "hashes": [
+ "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
+ "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.3"
+ },
+ "sphinxcontrib-serializinghtml": {
+ "hashes": [
+ "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
+ "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.1.4"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
+ "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.26.4"
+ }
+ },
+ "develop": {
+ "alabaster": {
+ "hashes": [
+ "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
+ "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
+ ],
+ "version": "==0.7.12"
+ },
+ "babel": {
+ "hashes": [
+ "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5",
+ "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.9.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
+ "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
+ ],
+ "version": "==2020.12.5"
+ },
+ "chardet": {
+ "hashes": [
+ "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
+ "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==4.0.0"
+ },
+ "click": {
+ "hashes": [
+ "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
+ "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==7.1.2"
+ },
+ "colorama": {
+ "hashes": [
+ "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
+ "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==0.4.4"
+ },
+ "docutils": {
+ "hashes": [
+ "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
+ "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==0.16"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+ "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.10"
+ },
+ "imagesize": {
+ "hashes": [
+ "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
+ "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.2.0"
+ },
+ "jinja2": {
+ "hashes": [
+ "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
+ "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.11.3"
+ },
+ "livereload": {
+ "hashes": [
+ "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"
+ ],
+ "version": "==2.6.3"
+ },
+ "lxml": {
+ "hashes": [
+ "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d",
+ "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37",
+ "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01",
+ "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2",
+ "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644",
+ "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75",
+ "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80",
+ "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2",
+ "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780",
+ "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98",
+ "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308",
+ "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf",
+ "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388",
+ "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d",
+ "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3",
+ "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8",
+ "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af",
+ "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2",
+ "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e",
+ "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939",
+ "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03",
+ "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d",
+ "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a",
+ "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5",
+ "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a",
+ "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711",
+ "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf",
+ "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089",
+ "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505",
+ "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b",
+ "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f",
+ "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc",
+ "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e",
+ "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931",
+ "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc",
+ "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe",
+ "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"
+ ],
+ "index": "pypi",
+ "version": "==4.6.2"
+ },
+ "markupsafe": {
+ "hashes": [
+ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
+ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
+ "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
+ "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
+ "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
+ "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f",
+ "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39",
+ "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
+ "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
+ "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014",
+ "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f",
+ "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
+ "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
+ "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
+ "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
+ "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
+ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
+ "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
+ "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
+ "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85",
+ "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1",
+ "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
+ "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
+ "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
+ "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850",
+ "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0",
+ "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
+ "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
+ "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb",
+ "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
+ "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
+ "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
+ "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1",
+ "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2",
+ "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
+ "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
+ "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
+ "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
+ "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
+ "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
+ "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
+ "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
+ "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
+ "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
+ "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
+ "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
+ "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
+ "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
+ "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
+ "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
+ "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
+ "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.1.1"
+ },
+ "packaging": {
+ "hashes": [
+ "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
+ "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.9"
+ },
+ "pygments": {
+ "hashes": [
+ "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94",
+ "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==2.8.1"
+ },
+ "pyparsing": {
+ "hashes": [
+ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
+ "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
+ ],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.4.7"
+ },
+ "pytz": {
+ "hashes": [
+ "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
+ "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
+ ],
+ "version": "==2021.1"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
+ "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.25.1"
+ },
+ "six": {
+ "hashes": [
+ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
+ "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.15.0"
+ },
+ "snowballstemmer": {
+ "hashes": [
+ "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2",
+ "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"
+ ],
+ "version": "==2.1.0"
+ },
+ "sphinx": {
+ "hashes": [
+ "sha256:672cfcc24b6b69235c97c750cb190a44ecd72696b4452acaf75c2d9cc78ca5ff",
+ "sha256:ef64a814576f46ec7de06adf11b433a0d6049be007fefe7fd0d183d28b581fac"
+ ],
+ "index": "pypi",
+ "version": "==3.5.2"
+ },
+ "sphinx-autobuild": {
+ "hashes": [
+ "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac",
+ "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"
+ ],
+ "index": "pypi",
+ "version": "==2021.3.14"
+ },
+ "sphinx-intl": {
+ "hashes": [
+ "sha256:2ff97cba0e4e43249e339a3c29dd2f5b63c25ce794050aabca320ad95f5c5b55",
+ "sha256:b25a6ec169347909e8d983eefe2d8adecb3edc2f27760db79b965c69950638b4"
+ ],
+ "index": "pypi",
+ "version": "==2.0.1"
+ },
+ "sphinxcontrib-applehelp": {
+ "hashes": [
+ "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
+ "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.2"
+ },
+ "sphinxcontrib-devhelp": {
+ "hashes": [
+ "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
+ "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.2"
+ },
+ "sphinxcontrib-htmlhelp": {
+ "hashes": [
+ "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
+ "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.3"
+ },
+ "sphinxcontrib-jsmath": {
+ "hashes": [
+ "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
+ "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.1"
+ },
+ "sphinxcontrib-qthelp": {
+ "hashes": [
+ "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
+ "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.3"
+ },
+ "sphinxcontrib-serializinghtml": {
+ "hashes": [
+ "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
+ "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.1.4"
+ },
+ "tornado": {
+ "hashes": [
+ "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb",
+ "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c",
+ "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288",
+ "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95",
+ "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558",
+ "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe",
+ "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791",
+ "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d",
+ "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326",
+ "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b",
+ "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4",
+ "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c",
+ "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910",
+ "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5",
+ "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c",
+ "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0",
+ "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675",
+ "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd",
+ "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f",
+ "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c",
+ "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea",
+ "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6",
+ "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05",
+ "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd",
+ "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575",
+ "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a",
+ "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37",
+ "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795",
+ "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f",
+ "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32",
+ "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c",
+ "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01",
+ "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4",
+ "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2",
+ "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921",
+ "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085",
+ "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df",
+ "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102",
+ "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5",
+ "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68",
+ "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==6.1"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
+ "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.26.4"
+ }
+ }
+}
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index f3d2650..0000000
--- a/_config.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-include:
- - _downloads
- - _images
- - _sources
- - _static
diff --git a/build-multilang.py b/build-multilang.py
new file mode 100755
index 0000000..7f8348a
--- /dev/null
+++ b/build-multilang.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+
+import os
+import re
+import shutil
+import subprocess
+import sys
+
+from glob import glob
+from pathlib import Path
+from lxml import html, etree
+
+base_lang: str = "ru"
+build_dir: str = "build"
+per_lang_static: list[str] = [
+ r"documentation_options\.js",
+ r"language_data\.js",
+ r"^(?!basic)\w+-stemmer\.js",
+ r"translations\.js",
+]
+
+# Cleanup
+shutil.rmtree(build_dir, True)
+os.mkdir(build_dir)
+
+# Build languages
+def sphinx_build(lang: str) -> None:
+ subprocess.run([
+ sys.executable, "-m", "sphinx",
+ "-b", "html",
+ "-D", f"language={lang}",
+ "source",
+ f"{build_dir}/{lang}",
+ ])
+
+languages: list[str] = [base_lang]
+if os.path.isdir("locale"):
+ languages += os.listdir("locale")
+
+index_page_lang: str = "en" if "en" in languages else base_lang
+
+for lang in languages:
+ sphinx_build(lang)
+
+# Extract common assets
+for i, lang in enumerate(languages):
+ if i == 0:
+ os.rename(f"{build_dir}/{lang}/_downloads", f"{build_dir}/_downloads")
+ os.rename(f"{build_dir}/{lang}/_images", f"{build_dir}/_images")
+ os.rename(f"{build_dir}/{lang}/CNAME", f"{build_dir}/CNAME")
+ os.rename(f"{build_dir}/{lang}/.nojekyll", f"{build_dir}/.nojekyll")
+ else:
+ shutil.rmtree(f"{build_dir}/{lang}/_downloads")
+ shutil.rmtree(f"{build_dir}/{lang}/_images")
+ os.remove(f"{build_dir}/{lang}/CNAME")
+ os.remove(f"{build_dir}/{lang}/.nojekyll")
+
+ for static_file in glob(f"{build_dir}/{lang}/_static/**/*", recursive=True):
+ if not os.path.isfile(static_file):
+ continue
+
+ relative_path = static_file.removeprefix(f"{build_dir}/{lang}/_static/")
+ matched: bool = False
+ for pattern in per_lang_static:
+ if re.match(pattern, relative_path):
+ matched = True
+ break
+
+ # I have no idea how to make "continue 2" from the loop above so...
+ if matched:
+ continue
+
+ dir = os.path.dirname(relative_path)
+ os.makedirs(f"{build_dir}/_static/{dir}", exist_ok=True)
+ os.rename(static_file, f"{build_dir}/_static/{relative_path}")
+
+ shutil.rmtree(f"{build_dir}/{lang}/_sources")
+ shutil.rmtree(f"{build_dir}/{lang}/.doctrees")
+ os.remove(f"{build_dir}/{lang}/.buildinfo")
+ os.remove(f"{build_dir}/{lang}/objects.inv")
+
+# Convert all relative routes into absolute
+def make_links_absolute(file_path: str, link: str) -> str:
+ # Skip local anchor links
+ if link.startswith("#"):
+ return link
+
+ # Skip external links (including links without protocol "//ely.by")
+ if link.startswith("//") or re.match(r"^https?://", link):
+ return link
+
+ if ".html" in link:
+ working_directory = os.getcwd()
+ os.chdir(Path(file_path).parent.absolute())
+ resolved = str(Path(link).resolve())
+ os.chdir(working_directory)
+
+ link = resolved.removeprefix(f"{working_directory}/{build_dir}")
+ link = link.replace("\\", "/") # fix for the Windows
+
+ return link
+
+ # Other links are links to some static assets
+ # There is no need to resolve relative links since _static is placed in the root directory,
+ # so after removing all ../ parts we can safely append / to make the path absolute
+ while link.startswith("../"):
+ link = link.removeprefix("../")
+
+ no_static_link = link.removeprefix("_static/")
+ for pattern in per_lang_static:
+ if re.match(pattern, no_static_link):
+ lang = re.match(fr"^{build_dir}/(\w+)/", file_path).group(1)
+
+ return f"/{lang}/{link}"
+
+ return f"/{link}"
+
+for file in glob(f"{build_dir}/**/*.html", recursive=True):
+ tree = html.parse(file) # type: etree._ElementTree
+ root = tree.getroot() # type: html.HtmlElement
+
+ root.rewrite_links(lambda link: make_links_absolute(file, link))
+
+ tree.write(file, method="html", encoding="UTF-8")
+
+# Create index.html for the site root
+shutil.copyfile(f"{build_dir}/{index_page_lang}/index.html", f"{build_dir}/index.html")
+index_file_tree = html.parse(f"{build_dir}/index.html") # type: etree._ElementTree
+index_file_root = index_file_tree.getroot() # type: html.HtmlElement
+index_file_last_toctree = index_file_root.find_class("toctree-wrapper")[0] # type: html.HtmlElement
+
+for lang in languages:
+ if lang == index_page_lang:
+ continue
+
+ tree = html.parse(f"{build_dir}/{lang}/index.html") # type: etree._ElementTree
+ root = tree.getroot() # type: html.HtmlElement
+
+ toctree = root.find_class("toctree-wrapper")[0] # type: html.HtmlElement
+ index_file_last_toctree.addnext(toctree)
+
+ index_file_last_toctree = index_file_root.find_class("toctree-wrapper")[-1] # type: html.HtmlElement
+
+index_file_tree.write(f"{build_dir}/index.html", method="html", encoding="UTF-8")
+
+# Add cross-lang links to the sidebar
+sidebar_menus: dict[str, html.HtmlElement] = {}
+for lang in languages:
+ tree = html.parse(f"{build_dir}/{lang}/index.html") # type: etree._ElementTree
+ root = tree.getroot() # type: html.HtmlElement
+
+ sidebar_menus[lang] = root.find_class("wy-menu")[0]
+
+for file in glob(f"{build_dir}/**/*.html") + [f"{build_dir}/index.html"]:
+ result = re.match(fr"^{build_dir}/(\w+)/", file)
+ lang: str = result.group(1) if result is not None else index_page_lang
+
+ tree = html.parse(file) # type: etree._ElementTree
+ root = tree.getroot() # type: html.HtmlElement
+
+ sidebar_menu_last = root.find_class("wy-menu")[0] # type: html.HtmlElement
+ for menuLang in sidebar_menus:
+ if menuLang == lang:
+ continue
+
+ sidebar_menu_last.addnext(sidebar_menus[menuLang])
+ sidebar_menu_last = root.find_class("wy-menu")[-1] # type: html.HtmlElement
+
+ tree.write(file, method="html", encoding="UTF-8")
diff --git a/crowdin.yml b/crowdin.yml
new file mode 100644
index 0000000..2a3bf0c
--- /dev/null
+++ b/crowdin.yml
@@ -0,0 +1,8 @@
+api_token_env: CROWDIN_PERSONAL_TOKEN
+base_url: https://ely.crowdin.com
+project_id : 2
+preserve_hierarchy: true
+
+files:
+ - source: build/locale/*.pot
+ translation: locale/%two_letters_code%/LC_MESSAGES/%file_name%.po
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index d4e27e6..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Sphinx~=3.0.0
-sphinx-rtd-theme~=0.4.3
-sphinx-autobuild~=0.7.1
diff --git a/source/_templates/layout.html b/source/_templates/layout.html
index b47d6c2..9d39c7d 100644
--- a/source/_templates/layout.html
+++ b/source/_templates/layout.html
@@ -17,4 +17,3 @@
{% endblock %}
-
diff --git a/source/api.html b/source/api.html
deleted file mode 100644
index a38349b..0000000
--- a/source/api.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
diff --git a/source/ru/api.rst b/source/api.rst
similarity index 59%
rename from source/ru/api.rst
rename to source/api.rst
index 8770110..5de6e25 100644
--- a/source/ru/api.rst
+++ b/source/api.rst
@@ -1,38 +1,25 @@
Ely.by API (симуляция Mojang API)
---------------------------------
-Здесь приведена информация об API, совместимом с функционалом `Mojang Api `_. Обращаем ваше
-внимание на то, что это не полноценное API Ely.by, а только набор дополнительных запросов, реализованных на базе нашего
-`сервера авторизации `_.
-
-Заметки
-=======
-
-* API не имеет ограничения на количество запросов. У нас есть просто настроенный fail2ban, который будет банить особо
- надоедливых клиентов. Такие дела.
+Здесь приведена информация об API, совместимом с функционалом `Mojang Api `_. Обращаем ваше внимание на то, что это не полноценное API Ely.by, а только набор дополнительных запросов, реализованных на базе нашего :doc:`сервера авторизации `.
Запросы
=======
-В этой секции будут описаны запросы и их же варианты для Mojang API. Все запросы выполняются на базовый url
-``https://authserver.ely.by``.
+.. note:: API не имеет ограничения на количество запросов. У нас есть просто настроенный fail2ban, который будет банить особо надоедливых клиентов. Такие дела.
+
+В этой секции будут описаны запросы и их же варианты для Mojang API. Все запросы выполняются на базовый url ``https://authserver.ely.by``.
UUID по нику на время
~~~~~~~~~~~~~~~~~~~~~
-Данный запрос позволяет узнать UUID пользователя по его нику на указанный момент времени. Время задаётся через GET
-параметр at с unitx timestamp.
-
-.. note:: На самом деле Ely.by пока не запоминает период смены ника и не обращает внимание на этот запрос. Тем не менее
- параметр в будущем будет дореализован.
+Данный запрос позволяет узнать UUID пользователя по его нику на указанный момент времени. Время задаётся через GET параметр at с unix timestamp.
.. function:: GET /api/users/profiles/minecraft/{username}
- Где username - искомый ник пользователя. Он может быть передан в любом регистре (В Mojang API только строгое
- совпадение).
+ Где ``{username}`` — искомый ник пользователя. Он может быть передан в любом регистре (В Mojang API только строгое совпадение).
- Обратите так же внимание, что параметры legacy и demo никогда не будут возвращены, т.к. эти параметры не имеют в Ely
- альтернативы и специфичны только для сервисов Mojang.
+ Обратите так же внимание, что параметры legacy и demo никогда не будут возвращены, т.к. эти параметры не имеют в Ely альтернативы и специфичны только для сервисов Mojang.
В случае успешного запроса вы получите следующий ответ сервера:
@@ -43,7 +30,7 @@ UUID по нику на время
"name": "ErickSkrauch"
}
-В случае, если переданный ник не будет найден, вы получите ответ с 204 статусом и пустым телом.
+В случае, если переданный ник не будет найден, вы получите ответ с ``204`` статусом и пустым телом.
Никнейм по UUID + история изменений
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -52,8 +39,7 @@ UUID по нику на время
.. function:: GET /api/user/profiles/{uuid}/names
- Где uuid - валиданый UUID. Валидным будет считаться UUID, написанный через дефисы или без них. В случае передачи
- невалидной строки, будет возвращён IllegalArgumentException_ с сообщением ``"Invalid uuid format."``.
+ Где ``{uuid}`` — валидный UUID. Валидным будет считаться UUID, написанный через дефисы или без них. В случае передачи невалидной строки, будет возвращён IllegalArgumentException_ с сообщением ``"Invalid uuid format."``.
В случае успешного запроса вы получите следующий ответ сервера:
@@ -69,10 +55,9 @@ UUID по нику на время
}
]
-.. note:: Т.к. на Ely.by не реализован алгоритм запоминания момента смены ника, будет возвращаться только 1 элемент.
- Чуть позже мы добавим полноценную поддержку запоминания момента смены ника.
+.. note:: Т.к. на Ely.by не реализован алгоритм запоминания момента смены ника, будет возвращаться только 1 элемент. Чуть позже мы добавим полноценную поддержку запоминания момента смены ника.
-В случае, если переданный UUID не будет найден, вы получите ответ с 204 статусом и пустым телом.
+В случае, если переданный UUID не будет найден, вы получите ответ с ``204`` статусом и пустым телом.
Список никнеймов в их UUID
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -83,10 +68,7 @@ UUID по нику на время
В теле запроса или POST параметрах необходимо передать валидный JSON массив искомых ников.
- В массиве должно быть не более 100 ников, в противном случае будет возвращён IllegalArgumentException_ с сообщением
- ``"Not more that 100 profile name per call is allowed."``. В случае, если переданная строка окажется невалидным
- JSON объектом, будет возвращёно это же исключение, только с текстом ``"Passed array of profile names is an invalid
- JSON string."``.
+ В массиве должно быть не более 100 ников, в противном случае будет возвращён IllegalArgumentException_ с сообщением ``"Not more that 100 profile name per call is allowed."``. В случае, если переданная строка окажется невалидным JSON объектом, будет возвращено это же исключение, только с текстом ``"Passed array of profile names is an invalid JSON string."``.
Пример тела запроса:
@@ -115,13 +97,12 @@ UUID по нику на время
Данные возвращаются в том же порядке, в каком и были запрошены.
-В случае, если один из переданных никнеймов не найден в базе данных, для него не будет возвращено значения (он будет
-просто пропущен). Учитывайте эту ситуацию при парсинге ответа.
+В случае, если один из переданных никнеймов не найден в базе данных, для него не будет возвращено значения (он будет просто пропущен). Учитывайте эту ситуацию при парсинге ответа.
Запрос информации о профиле по UUID
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-См. `запрос профиля для сервера авторизации `_.
+См. :ref:`запрос профиля для сервера авторизации `.
Возможные ошибки
================
@@ -137,11 +118,9 @@ IllegalArgumentException
.. code-block:: javascript
- // Пример ошибки при неправильном формате UUID
{
"error": "IllegalArgumentException",
"errorMessage": "Invalid uuid format."
}
-``errorMessage`` не всегда совпадает с таковым у Mojang, но в основном это касается только специфичных только для Ely
-ошибок. Оригинальные же запросы и ожидаемые от них ошибки повторяют тексты Mojang.
+``errorMessage`` не всегда совпадает с таковым у Mojang, но в основном это касается только специфичных только для Ely ошибок. Оригинальные же запросы и ожидаемые от них ошибки повторяют тексты Mojang.
diff --git a/source/ru/authlib-injector.rst b/source/authlib-injector.rst
similarity index 98%
rename from source/ru/authlib-injector.rst
rename to source/authlib-injector.rst
index 31293b2..db46951 100644
--- a/source/ru/authlib-injector.rst
+++ b/source/authlib-injector.rst
@@ -24,7 +24,7 @@ Authlib-injector
Если вы запускаете игру через лаунчер, то в его настройках необходимо найти поле для указания дополнительных аргументов JVM, куда необходимо в самое начало вставить строку, приведённую выше.
-.. figure:: ../_static/authlib-injector/launcher-jvm-options.png
+.. figure:: images/authlib-injector/launcher-jvm-options.png
:align: center
:alt: Редактирование аргументов JVM
@@ -40,7 +40,7 @@ Authlib-injector
При запуске сервера вы должны увидеть сообщение об активации authlib-injector:
-.. figure:: ../_static/authlib-injector/server-startup-messages.png
+.. figure:: images/authlib-injector/server-startup-messages.png
:align: center
:alt: Сообщение при запуске сервера
diff --git a/source/conf.py b/source/conf.py
index ff37718..8b187e2 100644
--- a/source/conf.py
+++ b/source/conf.py
@@ -29,7 +29,7 @@ import datetime
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = []
+extensions = ['sphinx.ext.ifconfig']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -45,7 +45,7 @@ master_doc = 'index'
# General information about the project.
project = 'Ely.by Documentation'
-copyright = str(datetime.datetime.now().year) + ', ErickSkrauch'
+copyright = str(datetime.datetime.now().year) + ', Ely.by'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -58,7 +58,7 @@ release = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-language = 'en'
+language = 'ru'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@@ -258,22 +258,24 @@ texinfo_documents = [
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
+# -- Options for sphinx-intl ----------------------------------------------
+
+locale_dirs = ['../locale/']
+gettext_compact = False
+
+# -- Static files generation rules ----------------------------------------
+
static_files = [
'../CNAME',
- '../_config.yml',
- 'api.html',
- 'minecraft-auth.html',
- 'oauth.html',
- 'skin-system.html',
+ '../.nojekyll',
]
from shutil import copyfile
from os import path
from sphinx.application import Sphinx
-def copy_static_files(app, _):
- # type: (Sphinx, None) -> None
- if app.builder.name != 'html':
+def copy_static_files(app: Sphinx, _) -> None:
+ if app.builder.name != "html":
return
for src_name in static_files:
@@ -286,4 +288,4 @@ def copy_static_files(app, _):
copyfile(src_path, target_path)
def setup(app):
- app.connect('build-finished', copy_static_files)
+ app.connect("build-finished", copy_static_files)
diff --git a/source/en/authlib-injector.rst b/source/en/authlib-injector.rst
deleted file mode 100644
index 566c847..0000000
--- a/source/en/authlib-injector.rst
+++ /dev/null
@@ -1,83 +0,0 @@
-Authlib-injector
-----------------
-
-**authlib-injector** is a library that allows you to spoof authorization and session server addresses in the Authlib, without modifying the library itself. It's designed as an javaagent.
-
-This library significantly simplifies the installation of an alternative authorization service in the game client and server, since transformation occurs during application bootstrap process.
-
-You can download the latest version from the `releases page on GitHub `_.
-
-Here is the documentation of the key aspects of installing and using the library. For more information, see the `original documentation in Chinese `_.
-
-.. _client:
-
-Installing in a game client
-===========================
-
-.. attention:: This section describes how to install the authlib-injector into the game. The game launcher still needs to implement the authorization flow itself in order to pass the ``accessToken`` to the game.
-
-To install the library, you need to specify it as a javaagent for the game. You can do this by prepending the line ``-javaagent:/path/to/file/authlib-injector.jar=ely.by`` as a game launching param. As the result, the launch command should look like this:
-
-.. code-block::
-
- java -javaagent:/path/to/file/authlib-injector.jar=ely.by -jar minecraft.jar
-
-If you run the game through a launcher, then in its settings you need to find a field for specifying additional JVM arguments, in which you need to insert the line above at the beginning.
-
-.. figure:: ../_static/authlib-injector/launcher-jvm-options.png
- :align: center
- :alt: Editing JVM arguments
-
-.. _server:
-
-Installing on a server
-======================
-
-Just as in the case with the game client, the library must be specified as javaagent. `Download the library `_ and put in the server's directory. Then add the javaagent call to the server launch command:
-
-| Before: ``java -jar minecraft_server.jar``
-| After: ``java -javaagent:authlib-injector.jar=ely.by -jar minecraft_server.jar``
-
-During server startup you should see a message about the activation of the authlib-injector:
-
-.. figure:: ../_static/authlib-injector/server-startup-messages.png
- :align: center
- :alt: Message at server startup
-
-BungeeCord
-~~~~~~~~~~
-
-The authlib-injector must be installed directly on the BungeeCord itself, as well as **on all backends** behind it. Note the configuration of the online-mode parameter:
-
-* The BungeeCord's configuration (``config.yml``) should contain ``online_mode=true``;
-* The servers behind the proxy must contain in their configuration (``server.properties``) the value ``online-mode=false``.
-
-Using such configuration authorization will work for all logging in players and the internal servers will correctly display player skins.
-
-LaunchHelper
-~~~~~~~~~~~~
-
-Not all game hostings allow direct modifications of launch arguments. To get around this limitation, you can use a special server that runs the game server by mixing authlib-injector into it. To install, follow these instructions:
-
-#. Download the corresponding LaunchHelper for your operating system from the `releases page `_.
-
-#. Upload this file and the ``authlib-injector.jar`` file to the server folder on your hosting site.
-
-#. Also create a ``launchhelper.properties`` file and put the following contents into it:
-
- .. code-block::
-
- javaAgentJarPath=authlib-injector.jar
- javaAgentOptions=ely.by
- execJarPath=minecraft_server.jar
-
- Where ``javaAgentJarPath`` contains the path to the authlib-injector.jar file and ``execJarPath`` contains the name of the server file.
-
-#. In the hosting control panel, specify the ``LaunchHelper.jar`` as the server file.
-
- If you can't change the executable file, you should rename the ``LaunchHelper.jar`` file to match your hosting requirements (usually, ``server.jar``). In this case, you should have the following file structure:
-
- * ``server.jar`` - the LaunchHelper file.
- * ``minecraft_server.jar`` - your server core.
- * ``authlib-injector.jar`` - the authlib-injector file.
- * ``launchhelper.properties`` - the configuration file for the LaunchHelper.
diff --git a/source/en/oauth.rst b/source/en/oauth.rst
deleted file mode 100644
index fa75af8..0000000
--- a/source/en/oauth.rst
+++ /dev/null
@@ -1,424 +0,0 @@
-Authorization via OAuth2 protocol
----------------------------------
-
-On this page you'll find how to implement OAuth2 authorization on your project through the Ely.by Accounts service.
-The implementation of this protocol will allow your users to authorize using their Ely.by account.
-
-Application registration
-========================
-
-First you need to `create a new application `_. Select **Website** as the
-application type. For the *Redirect URI* you can get away with just specifying the domain, but to increase security
-it's advised to use the full redirect path. Here are examples of valid addresses:
-
-* :samp:`http://site.com`
-* :samp:`http://site.com/oauth/ely`
-* :samp:`http://site.com/oauth.php?provider=ely`
-
-After a successful creation of an application, you'll be taken to the page containing a list of all your applications.
-If you click on the name of an application you'll see its ``clientId`` identifier and its ``clientSecret`` secret.
-They'll become important in later steps.
-
-Authorization initiation
-========================
-
-To initiate the authorization flow, you'll have to redirect the user to the following URL:
-
-.. code-block:: text
-
- https://account.ely.by/oauth2/v1?client_id=&redirect_uri=&response_type=code&scope=
-
-.. list-table:: Valid query parameters
- :widths: 1 1 98
- :header-rows: 1
-
- * - Parameter
- - Value example
- - Description
- * - *clientId*
- - :samp:`ely`
- - **Required**. ClientId that was received during registration.
- * - *redirect_uri*
- - :samp:`http://site.com/oauth.php`
- - **Required**. Return-forwarding address, which is matches the address specified during the application
- registration.
- * - *response_type*
- - :samp:`code`
- - **Required**. Response type. At the moment, only ``code`` is supported.
- * - *scope*
- - :samp:`account_info account_email`
- - **Required**. The list of permissions that you want to access, separated by spaces. See all available permissions
- in the `section below <#available-scopes>`_.
- * - *state*
- - :samp:`isfvubuysdboinsbdfvit`
- - Randomly generated string. Used as a session identifier to increase security. Will be returned unchanged after
- authorization is completed.
- * - *description*
- - :samp:`यो अनुप्रयोग विवरण`
- - If your application is available in several languages, you can use this field to override the default description
- in accordance with user's preferred language.
- * - *prompt*
- - :samp:`consent` or :samp:`select_account`
- - Forcibly display the request for permissions (``consent``) or forcibly request an account selection
- (``select_account``).
- * - *login_hint*
- - :samp:`erickskrauch` or :samp:`erickskrauch@ely.by`
- - If a user has several accounts, then specifying username or user email in this parameter will automatically
- select corresponding account. This is useful in a case of re-login after the token has expired.
-
-.. _available_scopes:
-.. list-table:: List of available scopes
- :widths: 1 99
- :header-rows: 0
-
- * - **account_info**
- - Get user information.
- * - **account_email**
- - Response to a request for user information will also contain user's email address.
- * - **offline_access**
- - With an ``access_token`` you will also recieve a ``refresh_token``. See more at
- `the corresponding section <#refresh-token-grant>`_.
- * - **minecraft_server_session**
- - It will be possible to use ``access_token`` as a session identifier for the Minecraft.
-
-------------------------------------------------------------------------------------------------------------------------
-
-After creating the link, place it in your template:
-
-.. code-block:: html
-
- Login via Ely.by
-
-After clicking on the URL a user will be redirected to our login page after which they'll be redirected back to the
-address specified in the ``redirect_uri`` parameter.
-
-Reverse redirection returns as ``?code=&state=`` for a successful authorization and
-``&error_message=`` for a failed one.
-
-Examples of successful and unsuccessful redirects:
-
-.. code-block:: text
-
- http://site.com/oauth/ely.php?code=dkpEEVtXBdIcgdQWak4SOPEpTJIvYa8KIq5cW9GJ&state=ajckasdcjasndckbsadc
- http://site.com/oauth/ely.php?error=access_denied&error_message=The+resource+owner+or+authorization+server+denied+the+request.
-
-.. _authorization-code-grant:
-
-Exchange auth code for a access key
-===================================
-
-After receiving an authorization code (``auth_code``), you'll need to exchange it for an authorization key
-(``access_key``). To do this, you must perform a POST request to the URL:
-
-.. code-block:: text
-
- https://account.ely.by/api/oauth2/v1/token
-
-And pass in following parameters:
-
-.. list-table::
- :widths: 1 99
- :header-rows: 0
-
- * - ``client_id``
- - ClientID that was received during registration.
- * - ``client_secret``
- - ClientSecret that was received during application registration.
- * - ``redirect_uri``
- - The exact URI that was used for user redirection.
- * - ``grant_type``
- - In this case, ``authorization_code`` should be used.
-
-**An example of the exchange in PHP:**
-
-.. code-block:: php
-
- 'ely', // Your ClientId that was received during registration
- 'client_secret' => 'Pk4uCtZw5WVlSUpvteJuTZkVqHXZ6aNtTaLPXa7X', // Your ClientSecret that was received during registration
- 'redirect_uri' => 'http://someresource.by/oauth/some.php', // Address where you expect to get a user back (current url)
- 'grant_type' => 'authorization_code',
- ];
-
- // If an error occurs, then the script will stop its execution
- if (isset($_GET['error'])) {
- echo $_GET['error_message'];
- return;
- }
-
- // We execute the code below only if the authorization code have arrived
- if (!is_null($_GET['code'])) {
- $oauthParams['code'] = $_GET['code'];
-
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, 'https://account.ely.by/api/oauth2/v1/token');
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_POST, true);
- curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($oauthParams));
- $out = json_decode(curl_exec($curl), true);
- curl_close($curl);
- }
-
-Notes to the code:
-
-* First, we declare the ``$oauthParams`` variable which will store the values that we got after registering the
- application.
-
-* Then we check if there was an error. In which case, we immediately stop the execution.
-
-* Then we create a POST request to exchange the ``code`` for an ``access_token``, passing all required fields in the
- process.
-
-* Then we execute the request, get the answer and parse it from JSON into the associative array.
-
-.. _authorization-code-grant-response:
-
-Server response
-~~~~~~~~~~~~~~~
-
-In case of a successful request, the response body will contain the result of exchanging the authorization code for an
-``access_token``. Data is a JSON document and can be easily interpreted by tools of a used programming language.
-
-The JSON document body will contain the following fields:
-
-.. code-block:: javascript
-
- {
- "access_token": "4qlktsEiwgspKEAotazem0APA99Ee7E6jNryVBrZ",
- "refresh_token": "m0APA99Ee7E6jNryVBrZ4qlktsEiwgspKEAotaze", // Presented only if the request had offline_access scope
- "token_type": "Bearer",
- "expires_in": 86400 // Number of seconds that token is active for
- }
-
-At this process authorization procedure is over. The resulting ``access_token`` can be used to obtain user information
-and to interact with our API.
-
-Getting user information
-========================
-
-If the received token has the ``account_info`` scope, then you can request information about the user's account.
-To do it, you have to send a request to the URL:
-
-.. code-block:: text
-
- https://account.ely.by/api/account/v1/info
-
-To send ``access_token``, the ``Authorization`` header is used with the value of ``Bearer {access_token}``.
-
-**An example of getting user information in PHP:**
-
-.. code-block:: php
-
- `_, but with the next parameters:
-
-.. list-table::
- :widths: 1 99
- :header-rows: 0
-
- * - ``client_id``
- - ClientClientID that was received during registration.
- * - ``client_secret``
- - ClientSecret that was received during application registration.
- * - ``scope``
- - The same scopes that were obtained for the initial access token. An attempt to extend this list will cause an
- error.
- * - ``refresh_token``
- - The token itself that was obtained along with the access token.
-
-**Example of a token refreshing in PHP:**
-
-.. code-block:: php
-
- 'ely', // Your ClientId, that was received during registration
- 'client_secret' => 'Pk4uCtZw5WVlSUpvteJuTZkVqHXZ6aNtTaLPXa7X', // Your ClientSecret, that was received during registration
- 'scope' => 'account_info account_email',
- 'refresh_token' => $refreshToken,
- 'grant_type' => 'refresh_token',
- ];
-
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, 'https://account.ely.by/api/oauth2/v1/token');
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_POST, true);
- curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($requestParams));
- $result = json_decode(curl_exec($curl), true);
- curl_close($curl);
-
-The answer will have exactly the same body as the result of
-`exchanging auto code for an access token <#authorization-code-grant-response>`_. The ``refresh_token`` field will be
-absent.
-
-Available libraries
-===================
-
-A simpler way is to use a ready-made library, to which you'll only have to provide registration parameters.
-Listed below are libraries for various programming languages. You can extend this list by providing your own library.
-
-* **PHP**:
-
- - [Official] https://github.com/elyby/league-oauth2-provider
-
-* **Ruby**:
-
- - [Official] https://github.com/elyby/omniauth-ely
-
-Possible errors
-================
-
-Below are the typical errors that you may receive after transmitting incorrect data to the authorization server.
-If you encounter an error that is not described in this documentation, please report it via
-`feedback form `_.
-
-.. _auth-start-errors:
-
-Errors during authorization initiation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This section describes the errors displayed when a user is redirected from your site to our authorization initiation
-page.
-
-.. code-block:: text
-
- Invalid request ({parameter} required).
-
-This error means that you did not pass all the required parameters. To solve this error just add the missing parameter.
-
-.. code-block:: text
-
- Invalid response type '{invalid_response_type_value}'.
-
-This error indicates that you passed an unsupported type of ``response_type``. Currently, the only supported value is
-``code``.
-
-.. code-block:: text
-
- Invalid scope '{invalid_scope}'.
-
-The error indicates that an unknown scope was requested. Make sure you request `supported scopes <#available-scopes>`_.
-
-.. code-block:: text
-
- Can not find application you are trying to authorize.
-
-This error indicates that the passed parameters do not correspond to any of the registered applications. To solve the
-problem, fix your ``client_id`` and ``redirect_uri`` values.
-
-.. _issue-token-errors:
-
-Errors when exchanging code for a key
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If an error occurs, instead of the expected response with the ``200`` status, you will receive a ``40x`` code and the
-following 2 fields:
-
-.. code-block:: json
-
- {
- "error": "invalid_request",
- "error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"code\" parameter."
- }
-
-The ``error`` field contains the system error identifier, and ``error_description`` describes the error in English
-language.
-
-**Possible error values:**
-
-.. list-table::
- :widths: 1 99
- :header-rows: 0
-
- * - ``invalid_request``
- - Not all the required request parameters were passed or the ``code`` value was not found in the issued codes
- database.
- * - ``unsupported_grant_type``
- - This error indicates that you tried to authorize using an unknown for our OAuth2 server Grant-type.
- * - ``invalid_client``
- - This error occurs when the trio of values ``client_id``, ``client_secret`` and ``redirect_uri`` didn't match
- with any of the registered applications.
-
-Errors when requesting user information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Response status ``401`` indicates that the ``Authorization`` header is not present in the request or its value formed
-incorrectly. The response body will be as follows:
-
-.. code-block:: json
-
- {
- "name": "Unauthorized",
- "status": 401,
- "message": "Your request was made with invalid credentials."
- }
-
-A response with the ``403`` status indicates that the token transferred in the ``Authorization`` header does not contain
-the ``account_info`` scope or it has expired. The response will be in the following format:
-
-.. code-block:: json
-
- {
- "name": "Forbidden",
- "status": 403,
- "message": "You are not allowed to perform this action."
- }
-
-Errors while updating access token
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When updating the access token you may encounter the same errors from
-`exchanging auth code for an access token <#issue-token-errors>`_, as well as several new ones:
-
-.. list-table::
- :widths: 1 99
- :header-rows: 0
-
- * - ``invalid_request``
- - Not all the required request parameters were passed or the ``refresh_token`` value wasn't found in the issued tokens database.
- * - ``invalid_scope``
- - The unsupported scope was listed or requested more scopes than the original token had.
diff --git a/source/en/skins-system.rst b/source/en/skins-system.rst
deleted file mode 100644
index 7881068..0000000
--- a/source/en/skins-system.rst
+++ /dev/null
@@ -1,201 +0,0 @@
-Skins system
-------------
-
-On this page you'll find information about available endpoints of Ely.by's skins system service. You can use any of
-them as an secondary or primary source of skins for your project.
-
-Ely.by's skins system service provides `proxying of textures from Minecraft premium users <#textures-proxy>`_,
-which means that using this service, your players will see both premium Minecraft users' skins and Ely.by users' skins.
-
-We strive to comply with the official skins system and do not support ears and HD-skins. The system supports capes,
-but doesn't allow players to wear them on their own.
-
-If you have suggestions for improving the existing functionality, please
-`create a new Issue `_ at the
-`Chrly project repository `_.
-
-.. note:: You can find more detailed information about the implementation of the skins system server in the
- `Chrly project repository `_.
-
-Requests URLs
-=============
-
-The skins system is located at the :samp:`http://skinsystem.ely.by` domain.
-
-In all queries, the :samp:`nickname` param must be replaced by the player's name. The value is case-insensitive.
-
-.. _skin-request:
-.. function:: /skins/{nickname}.png
-
- URL for downloading a skin texture. The :samp:`.png` extension can be omitted. If textures aren't found,
- the server will return a :samp:`404` status response.
-
-.. _cape-request:
-.. function:: /cloaks/{nickname}.png
-
- URL for downloading a cape texture. The :samp:`.png` extension can be omitted. If textures aren't found,
- the server will return a :samp:`404` status response.
-
-.. function:: /textures/{nickname}
-
- Via this URL you can get textures in the format specified in the :samp:`textures` field of JSON property with the
- same name in response to a
- `request for signed textures `_.
-
- .. code-block:: javascript
-
- {
- "SKIN": {
- "url": "http://example.com/skin.png",
- "metadata": {
- "model": "slim"
- }
- },
- "CAPE": {
- "url": "http://example.com/cape.png"
- }
- }
-
- Depending on the availability of textures for the player, fields :samp:`SKIN` or :samp:`CAPE` may be absent.
- Unless the skin model is :samp:`slim`, the :samp:`metadata` field will be omitted.
-
- The server will return an empty response with :samp:`204` status, if textures aren't found.
-
-.. function:: /profile/{nickname}
-
- This endpoint is an analog of the
- `player profile query in the Mojang's API `_, but
- instead of UUID user is queried by his nickname. Just like in the Mojang's API, you can append ``?unsigned=false``
- to the URL to get textures with a signature. The response will also include an additional property with ``name``
- **ely**.
-
- If the user has no textures, they'll be requested through the Mojang's API, but the Mojang's signature will be
- discarded and textures will be re-signed using `our signature key <#signature-verification-key-request>`_.
-
- .. code-block:: javascript
-
- {
- "id": "ffc8fdc95824509e8a57c99b940fb996",
- "name": "ErickSkrauch",
- "properties": [
- {
- "name": "textures",
- "signature": "eks3dLJWzod92dLfWH6Z8uc6l3+IvrZtTj3zjwnj0AdVt44ODKoL50N+RabYxf7zF3C7tlJwT1oAtydONrxXUarqUlpVeQzLlfsuqUKBLi0L+/Y9yQLG3AciNqzEWq3hYaOsJrsaJday/hQmKFnpXEFCThTMpSuZhoAZIiH4VG48NhP70U93ejyXF9b1nPYnXP6k7BVB8LYSzcjZfdqY88jQJbbvRzOyX14ZSD0Ma92jceLNKmkTVc2UfRLUNXtQKtVSFUzlAjCXPJW89IIOZTRqLg65qstWwBvn6VuikyUB5EIxM8vuCh7zTkrMOx1v2Q0xIj8YSFcbnBH2bo87SYOIe1bOK57ZEeUJqY6uSgMlWs7dI5D3nmhFptErm72hg55Axdo1xbG4mvnmLYF7SA4yMDSytPPL+kA+sw3pafnvU2IZo38gqJSDOOpkOpdhUoHx85fzRJL8AcLSJiFlCZDl4pSi3cVuKy/xY5ohT/fJ6GEqpbZp3gACymn47zzI42VSh6j1DQnx2wnhqalTv0kE3qpAFpK/htSboQkFCW/bULO3b+vgU87XPlReT7UtH4yGLtixgs5GC8AzBraN8vOMv8TZCX9ab6mBBjOoDJjXa8Tq637TC75GxRHlpAN2jRHYvyp2zJwjUrML3u4eD4osHW+VBfl8D2l3nLJuemQ=",
- "value": "eyJ0aW1lc3RhbXAiOjE2MTQ5MzczMjc0MzcsInByb2ZpbGVJZCI6ImZmYzhmZGM5NTgyNDUwOWU4YTU3Yzk5Yjk0MGZiOTk2IiwicHJvZmlsZU5hbWUiOiJFcmlja1NrcmF1Y2giLCJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly9lbHkuYnkvc3RvcmFnZS9za2lucy82OWM2NzQwZDI5OTNlNWQ2ZjZhN2ZjOTI0MjBlZmMyOS5wbmcifX19"
- },
- {
- "name": "ely",
- "value": "but why are you asking?"
- }
- ]
- }
-
- The server will return an empty response with ``204`` status if the nickname wasn't found locally nor via the
- Mojang's API.
-
-.. _signature-verification-key-request:
-.. function:: /signature-verification-key.der
-
- This endpoint returns a public key that can be used to verify a texture's signature. The key is provided in ``DER``
- format, so it can be used directly in the Authlib, without modifying the signature checking algorithm.
-
-.. function:: /signature-verification-key.pem
-
- The same endpoint as the previous one, except that it returns the key in ``PEM`` format.
-
-.. function:: /textures/signed/{nickname}
-
- This request is used in our `server skins system plugin `_ to load textures with
- the original Mojang's signature. The textures received this way can be transferred to an unmodified game client
- without any changes. The answer will also include additional property with :samp:`name` equal to **ely**.
-
- .. code-block:: javascript
-
- {
- "id": "ffc8fdc95824509e8a57c99b940fb996",
- "name": "ErickSkrauch",
- "properties": [
- {
- "name": "textures",
- "signature": "QH+1rlQJYk8tW+8WlSJnzxZZUL5RIkeOO33dq84cgNoxwCkzL95Zy5pbPMFhoiMXXablqXeqyNRZDQa+OewgDBSZxm0BmkNmwdTLzCPHgnlNYhwbO4sirg3hKjCZ82ORZ2q7VP2NQIwNvc3befiCakhDlMWUuhjxe7p/HKNtmKA7a/JjzmzwW7BWMv8b88ZaQaMaAc7puFQcu2E54G2Zk2kyv3T1Bm7bV4m7ymbL8McOmQc6Ph7C95/EyqIK1a5gRBUHPEFIEj0I06YKTHsCRFU1U/hJpk98xXHzHuULJobpajqYXuVJ8QEVgF8k8dn9VkS8BMbXcjzfbb6JJ36v7YIV6Rlt75wwTk2wr3C3P0ij55y0iXth1HjwcEKsg54n83d9w8yQbkUCiTpMbOqxTEOOS7G2O0ZDBJDXAKQ4n5qCiCXKZ4febv4+dWVQtgfZHnpGJUD3KdduDKslMePnECOXMjGSAOQou//yze2EkL2rBpJtAAiOtvBlm/aWnDZpij5cQk+pWmeHWZIf0LSSlsYRUWRDk/VKBvUTEAO9fqOxWqmSgQRUY2Ea56u0ZsBb4vEa1UY6mlJj3+PNZaWu5aP2E9Unh0DIawV96eW8eFQgenlNXHMmXd4aOra4sz2eeOnY53JnJP+eVE4cB1hlq8RA2mnwTtcy3lahzZonOWc=",
- "value": "eyJ0aW1lc3RhbXAiOjE0ODYzMzcyNTQ4NzIsInByb2ZpbGVJZCI6ImM0ZjFlNTZmNjFkMTQwYTc4YzMyOGQ5MTY2ZWVmOWU3IiwicHJvZmlsZU5hbWUiOiJXaHlZb3VSZWFkVGhpcyIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS83Mzk1NmE4ZTY0ZWU2ZDhlYzY1NmFkYmI0NDA0ZjhlYmZmMzQxMWIwY2I5MGIzMWNiNDc2ZWNiOTk2ZDNiOCJ9fX0="
- },
- {
- "name": "ely",
- "value": "but why are you asking?"
- }
- ]
- }
-
- By default textures proxying isn't used for this query. To enable it, add an additional GET parameter
- :samp:`?proxy=true`.
-
- The server will return an empty response with :samp:`204` status, if textures aren't found.
-
-------------------------------------------------------------------------------------------------------------------------
-
-You can also pass a range of additional GET parameters while making any of the above requests. They will be used
-to analyze the usage of the service by different versions of the game.
-
-:version: The version of the protocol by which skins will be requested. The current version is :samp:`2`,
- i.e. you need to specify :samp:`version=2`.
-
-:minecraft_version: The version of Minecraft that the request is made from.
-
-:authlib_version: The version of the Authlib used. This option is relevant for Minecraft versions 1.7.6+,
- where a separate library is used to load skins instead of in-game code.
-
-Here is an example of a textures request with parameters described above:
-
-.. code-block:: text
-
- http://skinsystem.ely.by/textures/erickskrauch?version=2&minecraft_version=1.14.0&authlib_version=1.5.25
-
-Additional URLs
-+++++++++++++++
-
-You can also perform a skin and cape request by passing the nickname through the GET parameter. This feature is used
-to pass analytical parameters of game versions up to 1.5.2, where the nickname is simply appended to the end of the
-line. To do this, the entire string is arranged in such a way that the last parameter is :samp:`name`, after appending
-a nickname to which you get a full request string for textures.
-
-.. function:: /skins?name={nickname}.png
-
- See the `skin request <#skin-request>`_.
-
-.. function:: /cloaks?name={nickname}.png
-
- See the `cape request <#cape-request>`_.
-
-Examples of requests for textures with parameters from above:
-
-.. code-block:: text
-
- http://skinsystem.ely.by/skins?version=2&minecraft_version=1.5.2&name=erickskrauch.png
- http://skinsystem.ely.by/cloaks?version=2&minecraft_version=1.4.7&name=notch
-
-.. _textures-proxy:
-
-Textures proxying
-=================
-
-Ely.by's skins system service obtains textures from the official skin system in a case where no information about
-textures for the requested username was found in the database. The request will also be proxied if a skin entry is
-found, but it's default.
-
-To improve the throughput of the proxying algorithm, information about textures is cached in 2 stages:
-
-* Player's names and UUIDs matches are stored
- `for 30 days `_.
-
-* Information about textures isn't updated more often than
- `once a minute `_.
-
-If you own a Minecraft premium account, but your nickname is busy, please contact our
-`support team `_ and after a short check we'll pass the nickname on to you.
-
-Ready-made implementations
-==========================
-
-Ready-made patch implementations and installation instructions can be found at the
-`download section of the main Ely.by website `_.
diff --git a/source/_static/minecraft-auth/authlib/authlib-1.3.1.jar b/source/files/authlib/authlib-1.3.1.jar
similarity index 100%
rename from source/_static/minecraft-auth/authlib/authlib-1.3.1.jar
rename to source/files/authlib/authlib-1.3.1.jar
diff --git a/source/_static/minecraft-auth/authlib/authlib-1.3.jar b/source/files/authlib/authlib-1.3.jar
similarity index 100%
rename from source/_static/minecraft-auth/authlib/authlib-1.3.jar
rename to source/files/authlib/authlib-1.3.jar
diff --git a/source/_static/authlib-injector/launcher-jvm-options.png b/source/images/authlib-injector/launcher-jvm-options.png
similarity index 100%
rename from source/_static/authlib-injector/launcher-jvm-options.png
rename to source/images/authlib-injector/launcher-jvm-options.png
diff --git a/source/_static/authlib-injector/server-startup-messages.png b/source/images/authlib-injector/server-startup-messages.png
similarity index 100%
rename from source/_static/authlib-injector/server-startup-messages.png
rename to source/images/authlib-injector/server-startup-messages.png
diff --git a/source/_static/minecraft-auth/authlib-install.png b/source/images/minecraft-auth/authlib-install.png
similarity index 100%
rename from source/_static/minecraft-auth/authlib-install.png
rename to source/images/minecraft-auth/authlib-install.png
diff --git a/source/_static/minecraft-auth/bungeecord_inclasstranslator.png b/source/images/minecraft-auth/bungeecord_inclasstranslator.png
similarity index 100%
rename from source/_static/minecraft-auth/bungeecord_inclasstranslator.png
rename to source/images/minecraft-auth/bungeecord_inclasstranslator.png
diff --git a/source/_static/minecraft-auth/bungeecord_move.png b/source/images/minecraft-auth/bungeecord_move.png
similarity index 100%
rename from source/_static/minecraft-auth/bungeecord_move.png
rename to source/images/minecraft-auth/bungeecord_move.png
diff --git a/source/_static/minecraft-auth/installing_by_inclasstranslator.png b/source/images/minecraft-auth/installing_by_inclasstranslator.png
similarity index 100%
rename from source/_static/minecraft-auth/installing_by_inclasstranslator.png
rename to source/images/minecraft-auth/installing_by_inclasstranslator.png
diff --git a/source/index.rst b/source/index.rst
index 52fbbc1..4869db3 100644
--- a/source/index.rst
+++ b/source/index.rst
@@ -1,21 +1,13 @@
-Welcome to the Ely.by documentation!
-====================================
+Добро пожаловать в документацию Ely.by!
+=======================================
-In this documentation you will find information about the public services of the Ely.by project, using which you'll be
-able to integrate your projects with the Ely.by services.
+В этой документации вы найдёте информацию о публичных сервисах проекта Ely.by, используя которые вы сможете самостоятельно реализовать свои программные продукты для совместной работы с сервисом Ely.by.
-You are free to improve this documentation in the `documentation's repository `_.
+Вы можете свободно улучшать и вносить предложения по изменениям в документацию в `репозитории проекта `_.
.. toctree::
- :caption: English articles:
:maxdepth: 2
- :glob:
-
- en/*
-
-.. toctree::
:caption: Статьи на русском:
- :maxdepth: 2
:glob:
- ru/*
+ *
diff --git a/source/minecraft-auth.html b/source/minecraft-auth.html
deleted file mode 100644
index 637887e..0000000
--- a/source/minecraft-auth.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
diff --git a/source/ru/minecraft-auth.rst b/source/minecraft-auth.rst
similarity index 76%
rename from source/ru/minecraft-auth.rst
rename to source/minecraft-auth.rst
index 8dcd9bd..c5b7b21 100644
--- a/source/ru/minecraft-auth.rst
+++ b/source/minecraft-auth.rst
@@ -3,8 +3,7 @@
Здесь приведена информация по авторизации для лаунчеров и серверов Minecraft через сервис авторизации Ely.by.
-Протокол авторизации реализован максимально похожим на `оригинальный протокол авторизации Mojang `_,
-но тем не менее эта документация описывает все доступные функции конкретно сервиса авторизации Ely.by.
+Протокол авторизации реализован максимально похожим на `оригинальный протокол авторизации Mojang `_, но тем не менее эта документация описывает все доступные функции конкретно сервиса авторизации Ely.by.
Общие положения
===============
@@ -13,8 +12,7 @@
* При успешном запросе, сервер вернёт ответ со статусом 200. Любой другой код свидетельствует об ошибке.
-* Сервер всегда отвечает JSON данными, кроме случаев системных ошибок и ответов на legacy запросы. Учитывайте это для
- отображения пользователю правильного сообщения об ошибке.
+* Сервер всегда отвечает JSON данными, кроме случаев системных ошибок и ответов на legacy запросы. Учитывайте это для отображения пользователю правильного сообщения об ошибке.
* В случае стандартной ошибки, вы получите следующие данные:
@@ -122,8 +120,7 @@
.. note:: В оригинальном протоколе так же передаётся значение ``selectedProfile``, но в реализации Mojang он не влияет ни на что. Наша реализация сервера авторизации игнорирует этот параметр и опирается на значения ``accessToken`` и ``clientToken``.
- В случае получения какой-либо предусмотренной ошибки, следует заново запросить пароль пользователя и произвести
- обычную авторизацию.
+ В случае получения какой-либо предусмотренной ошибки, следует заново запросить пароль пользователя и произвести обычную авторизацию.
Успешный ответ:
@@ -150,8 +147,7 @@
.. function:: POST /auth/validate
- Этот запрос позволяет проверить валиден ли указанный accessToken или нет. Этот запрос не обновляет токен и его время
- жизни, а только позволяет удостовериться, что он ещё действительный.
+ Этот запрос позволяет проверить валиден ли указанный accessToken или нет. Этот запрос не обновляет токен и его время жизни, а только позволяет удостовериться, что он ещё действительный.
:param string accessToken: Токен доступа, полученный после авторизации.
@@ -160,7 +156,7 @@
.. code-block:: javascript
{
- "error: "ForbiddenOperationException",
+ "error": "ForbiddenOperationException",
"errorMessage": "Token expired."
}
@@ -175,8 +171,7 @@
.. function:: POST /auth/invalidate
- Запрос позволяет инвалидировать accessToken. В случае, если переданный токен не удастся найти в хранилище токенов,
- ошибка не будет сгенерирована и вы получите успешный ответ.
+ Запрос позволяет инвалидировать accessToken. В случае, если переданный токен не удастся найти в хранилище токенов, ошибка не будет сгенерирована и вы получите успешный ответ.
Входные параметры:
@@ -188,18 +183,14 @@
Авторизация на сервере
======================
-Эти запросы выполняются непосредственно клиентом и сервером при помощи внутреннего кода или библиотеки authlib
-(начиная с версии 1.7.2). Они актуальны только в том случае, если вы уже произвели авторизацию и запустили игру с валидным
-accessToken. Вам остаётся только заменить пути внутри игры/библиотеки на привидённые ниже пути.
+Эти запросы выполняются непосредственно клиентом и сервером при помощи внутреннего кода или библиотеки authlib (начиная с версии 1.7.2). Они актуальны только в том случае, если вы уже произвели авторизацию и запустили игру с валидным accessToken. Вам остаётся только заменить пути внутри игры/библиотеки на приведённые ниже пути.
-Поскольку непосредственно изменить что-либо в работе authlib или игры вы не можете, здесь не приводятся передаваемые значения
-и ответы сервера. При необходимости вы сможете найти эту информацию самостоятельно в интернете.
+Поскольку непосредственно изменить что-либо в работе authlib или игры вы не можете, здесь не приводятся передаваемые значения и ответы сервера. При необходимости вы сможете найти эту информацию самостоятельно в интернете.
Через authlib
~~~~~~~~~~~~~
-.. important:: Эта часть документации описывает запросы, выполняемые через authlib в версии игры 1.7.2+. Для более старых
- версий смотрите раздел ниже.
+.. important:: Эта часть документации описывает запросы, выполняемые через authlib в версии игры 1.7.2+. Для более старых версий смотрите раздел ниже.
Все запросы из этой категории выполняются на подуровень /session. Перед каждым из запросов указан тип отправляемого запроса.
@@ -209,21 +200,14 @@ accessToken. Вам остаётся только заменить пути вн
.. function:: GET /session/hasJoined
- Запрос на этот URL выполняет сервер с online-mode=true после того, как клиент, пытающийся к нему подключится, успешно
- выполнит join запрос.
+ Запрос на этот URL выполняет сервер с online-mode=true после того, как клиент, пытающийся к нему подключится, успешно выполнит join запрос.
- .. attention:: Внутри тела ответа есть параметр **properties**, который, в свою очередь, содержит поле **value** с
- закодированной в ней base64 строкой. В оригинальной системе авторизации данные зашифрованы с помощью
- приватного ключа и расшифровывались на клиенте с помощью публичного.
-
- Ely, в свою очередь, **не выполняет шифрацию вовсе**, поэтому вам необходимо отключить проверку подписи в
- библиотеке authlib. В противном случае текстуры всегда будут признаваться невалидными.
+ .. attention:: Внутри тела ответа есть параметр **properties**, который, в свою очередь, содержит поле **value** с закодированной в ней base64 строкой. В оригинальной системе авторизации данные зашифрованы с помощью приватного ключа и расшифровывались на клиенте с помощью публичного. Ely, в свою очередь, **не выполняет шифрацию вовсе**, поэтому вам необходимо отключить проверку подписи в библиотеке authlib. В противном случае текстуры всегда будут признаваться невалидными.
Для старых версий
~~~~~~~~~~~~~~~~~
-.. important:: Эта часть документации описывает запросы, выполняемые более старыми версиями Minecraft, когда не применялась
- библиотека authlib. Это все версии, младше версии 1.7.2.
+.. important:: Эта часть документации описывает запросы, выполняемые более старыми версиями Minecraft, когда не применялась библиотека authlib. Это все версии, младше версии 1.7.2.
Все запросы из этой категории выполняются на подуровень /session/legacy. Перед каждым из запросов указан тип отправляемого запроса.
@@ -235,72 +219,55 @@ accessToken. Вам остаётся только заменить пути вн
.. function:: GET /session/legacy/hasJoined
- Запрос на этот URL выполняет сервер с online-mode=true после того, как клиент, пытающийся к нему подключится, успешно
- выполнит join запрос.
+ Запрос на этот URL выполняет сервер с online-mode=true после того, как клиент, пытающийся к нему подключится, успешно выполнит join запрос.
-Важно не потерять GET параметр **?user=** в конце обоих запросов, чтобы получились следующие URL:
-``http://minecraft.ely.by/session/legacy/hasJoined?user=``.
+Важно не потерять GET параметр **?user=** в конце обоих запросов, чтобы получились следующие URL: ``http://minecraft.ely.by/session/legacy/hasJoined?user=``.
Одиночная игра
==============
-По сути, одиночная игра - это локальный сервер, созданный для одного игрока. По крайней мере это так, начиная с версии 1.6,
-в которой и был представлен механизм локальных серверов.
+По сути, одиночная игра - это локальный сервер, созданный для одного игрока. По крайней мере это так, начиная с версии 1.6, в которой и был представлен механизм локальных серверов.
-Тем не менее, описанный ниже запрос актуален только для Minecraft 1.7.6+, когда для загрузки скинов стала использоваться
-так же authlib.
+Тем не менее, описанный ниже запрос актуален только для Minecraft 1.7.6+, когда для загрузки скинов стала использоваться так же Authlib.
.. _profile-request:
.. function:: GET /session/profile/{uuid}
- Запрос на этот URL выполняется клиентом в одиночной игре на локальном сервере (созданном посредством самой игры).
- В URL передаётся UUID пользователя, с которым был запущен клиент, а в ответ получается информация о текстурах игрока
- в таком же формате, как и при hasJoined запросе.
+ Запрос на этот URL выполняется клиентом в одиночной игре на локальном сервере (созданном посредством самой игры). В URL передаётся UUID пользователя, с которым был запущен клиент, а в ответ получается информация о текстурах игрока в таком же формате, как и при hasJoined запросе.
Готовые библиотеки authlib
==========================
-.. attention:: Ely.by поддрживает библиотеку authlib-injector. Это наиболее простой и универсальный способ установки системы авторизации в игру и игровые сервера. За подробностями обратитесь в :doc:`соответствующий раздел документации `.
+.. attention:: Ely.by поддерживает библиотеку authlib-injector. Это наиболее простой и универсальный способ установки системы авторизации в игру и игровые сервера. За подробностями обратитесь в :doc:`соответствующий раздел документации `.
-Поскольку самостоятельная реализация связана с трудностями поиска исходников, подключения зависимостей и в конце-концов
-с процессом компиляции, на `странице загрузок нашей системы скинов /ely.by/load>`_ вы можете загрузить уже
-готовые библиотеки со всеми необходимыми изменениями. Выберите в выпадающем списке необходимую версию и следуйте
-инструкции по установке, размещённой на той же странице ниже.
+Поскольку самостоятельная реализация связана с трудностями поиска исходников, подключения зависимостей и в конце-концов с процессом компиляции, на `странице загрузок нашей системы скинов `_ вы можете загрузить уже готовые библиотеки со всеми необходимыми изменениями. Выберите в выпадающем списке необходимую версию и следуйте инструкции по установке, размещённой на той же странице ниже.
-В более ранних версиях игры система скинов находилась внутри игрового клиента, так что библиотеки ниже обеспечивают
-лишь авторизацию:
+В более ранних версиях игры система скинов находилась внутри игрового клиента, так что библиотеки ниже обеспечивают лишь авторизацию:
-* Minecraft 1.7.5 - :download:`authlib 1.3.1 <../_static/minecraft-auth/authlib/authlib-1.3.1.jar>`
+* Minecraft 1.7.5 - :download:`authlib 1.3.1 `
-* Minecraft 1.7.2 - :download:`authlib 1.3 <../_static/minecraft-auth/authlib/authlib-1.3.jar>`
+* Minecraft 1.7.2 - :download:`authlib 1.3 `
-Для установки вам необходимо заменить оригинальную библиотеку, располагающуюся по пути
-``<директория установки minecraft>/libraries/com/mojang/authlib/``. Убедитесь в том, что версии скачанного и заменяемого
-файлов совпадают.
+Для установки вам необходимо заменить оригинальную библиотеку, располагающуюся по пути ``<директория установки minecraft>/libraries/com/mojang/authlib/``. Убедитесь в том, что версии скачанного и заменяемого файлов совпадают.
.. _install-server:
Установка authlib на сервер
===========================
-Сервер также использует authlib для выполнения авторизации игрока, поэтому соответствующие изменения должны быть
-также применены и к нему. Ниже приведены инструкции по установки authlib для различных реализаций сервера Minecraft.
+Сервер также использует authlib для выполнения авторизации игрока, поэтому соответствующие изменения должны быть также применены и к нему. Ниже приведены инструкции по установки authlib для различных реализаций сервера Minecraft.
-.. note:: Если ни одна из приведённых ниже инструкций не подошла для вашей реализации сервера, пожалуйста,
- создайте `новый issue `_ и мы допишем инструкцию для вашего сервера.
+.. note:: Если ни одна из приведённых ниже инструкций не подошла для вашей реализации сервера, пожалуйста, создайте `новый issue `_ и мы допишем инструкцию для вашего сервера.
.. _vanilla:
Оригинальный сервер
~~~~~~~~~~~~~~~~~~~
-С помощью архиватора откройте файл сервера ``minecraft_server.ВЕРСИЯ.jar``. Таким же образом откройте архив с
-authlib для соответствующей версии сервера. Перед вами будет два окна: одно с файлами сервера, другое с файлами authlib.
-Вам необходимо "перетащить" из архива с authlib все файлы и папки, **за исключением директории META-INF**, и подтвердить
-замену.
+С помощью архиватора откройте файл сервера ``minecraft_server.ВЕРСИЯ.jar``. Таким же образом откройте архив с authlib для соответствующей версии сервера. Перед вами будет два окна: одно с файлами сервера, другое с файлами authlib. Вам необходимо "перетащить" из архива с authlib все файлы и папки, **за исключением директории META-INF**, и подтвердить замену.
-.. figure:: ../_static/minecraft-auth/authlib-install.png
+.. figure:: images/minecraft-auth/authlib-install.png
:align: center
:alt: Процесс установки Authlib
@@ -311,10 +278,7 @@ authlib для соответствующей версии сервера. Пе
Bukkit/Spigot
~~~~~~~~~~~~~
-Сперва выполните установку, как она описана для `оригинального сервера <#vanilla>`_. Затем скачайте библиотеки
-`commons-io `_ и
-`commons-lang3 `_,
-после чего аналогичным с authlib образом последовательно переместите содержимое скачанных архивов в файлы сервера.
+Сперва выполните установку, как она описана для `оригинального сервера <#vanilla>`_. Затем скачайте библиотеки `commons-io `_ и `commons-lang3 `_, после чего аналогичным с authlib образом последовательно переместите содержимое скачанных архивов в файлы сервера.
Forge/Sponge
~~~~~~~~~~~~
@@ -330,9 +294,7 @@ Forge/Sponge
Paper (PaperSpigot)
~~~~~~~~~~~~~~~~~~~
-Установка производится по аналогии с `Bukkit/Spigot <#bukkit-spigot>`_ в файл ``cache/patched-ВЕРСИЯ.jar``.
-После внесения изменений, запускать сервер нужно через jar-файл из директории ``cache``, поскольку в противном случае
-**Paper восстановит исходное состояние файла**:
+Установка производится по аналогии с `Bukkit/Spigot <#bukkit-spigot>`_ в файл ``cache/patched-ВЕРСИЯ.jar``. После внесения изменений, запускать сервер нужно через jar-файл из директории ``cache``, поскольку в противном случае **Paper восстановит исходное состояние файла**:
| До: ``java -jar paper-ВЕРСИЯ-БИЛД.jar``
| После: ``java -jar cache/patched-ВЕРСИЯ.jar``
@@ -356,13 +318,13 @@ BungeeCord
#. Откройте распакованный файл в программе InClassTranslator и замените в нём строку ``https://sessionserver.mojang.com/session/minecraft/hasJoined?username=`` на ``https://authserver.ely.by/session/hasJoined?username=``, как показано на рисунке ниже:
- .. figure:: ../_static/minecraft-auth/bungeecord_inclasstranslator.png
+ .. figure:: images/minecraft-auth/bungeecord_inclasstranslator.png
:align: center
:alt: Редактирование в InClassTranslator
#. Сохраните изменения и перетащите измененный файл обратно в архив сервера. Подтвердите замену.
- .. figure:: ../_static/minecraft-auth/bungeecord_move.png
+ .. figure:: images/minecraft-auth/bungeecord_move.png
:align: center
:alt: Перетаскивание отредактированного файла назад в архив
@@ -375,12 +337,9 @@ BungeeCord
Установка на версии ниже 1.7.2
==============================
-Для более старых версий существует достаточно большое многообразие различных случаев, раскрыть которые в этой документации
-не представляется возможным. Вся установка заключается в замене определённых строк в определённых классах через
-InClassTranslator.
+Для более старых версий существует достаточно большое многообразие различных случаев, раскрыть которые в этой документации не представляется возможным. Вся установка заключается в замене определённых строк в определённых классах через InClassTranslator.
-На форуме RuBukkit есть отличный пост, в котором собрана вся нужна информация по именам классов на различных версиях
-Minecraft. Переписывать его сюда не имеет смысла, так что просто перейдите на его страницу и найдите нужную версию.
+На форуме RuBukkit есть отличный пост, в котором собрана вся нужна информация по именам классов на различных версиях Minecraft. Переписывать его сюда не имеет смысла, так что просто перейдите на его страницу и найдите нужную версию.
|rubukkit_link|.
@@ -394,21 +353,19 @@ Minecraft. Переписывать его сюда не имеет смысла
Предположим, что вы хотите установить авторизацию на сервер версии 1.5.2.
-Сначала вы переходите по вышепривидённой ссылке, выбираете нужную версию (1.5.2) и видите список классов:
+Сначала вы переходите по вышеприведённой ссылке, выбираете нужную версию (1.5.2) и видите список классов:
* **bdk.class** - путь до joinserver
* **jg.class** - путь до checkserver
-Затем вы должны взять .jar файл клиента и открыть его любым архиватором. После чего вам необходимо найти файл **bdk.class**.
-Для этого удобно воспользоваться поиском.
+Затем вы должны взять .jar файл клиента и открыть его любым архиватором. После чего вам необходимо найти файл **bdk.class**. Для этого удобно воспользоваться поиском.
После того, как вы нашли файл, его нужно извлечь из архива - просто перетащите его оттуда в удобную для вас дирикторию.
-Дальше запустите InClassTranslator и в нём откройте этот класс. Слева будет список найденных в файле строк, которые вы
-можете изменить. Нужно заменить только строку, отвечающую за запрос на подключение к серверу:
+Дальше запустите InClassTranslator и в нём откройте этот класс. Слева будет список найденных в файле строк, которые вы можете изменить. Нужно заменить только строку, отвечающую за запрос на подключение к серверу:
-.. figure:: ../_static/minecraft-auth/installing_by_inclasstranslator.png
+.. figure:: images/minecraft-auth/installing_by_inclasstranslator.png
:align: center
:alt: Порядок редактирования: выбрать нужную строку, изменить, сохранить.
@@ -418,5 +375,4 @@ Minecraft. Переписывать его сюда не имеет смысла
-----------------------
-После этих действий вам нужно в настройках включить online-mode=true и сервер станет пускать на себя только тех игроков,
-которые будут авторизованы через Ely.by.
+После этих действий вам нужно в настройках включить online-mode=true и сервер станет пускать на себя только тех игроков, которые будут авторизованы через Ely.by.
diff --git a/source/oauth.html b/source/oauth.html
deleted file mode 100644
index b4ec2a4..0000000
--- a/source/oauth.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
diff --git a/source/ru/oauth.rst b/source/oauth.rst
similarity index 74%
rename from source/ru/oauth.rst
rename to source/oauth.rst
index 7977387..f88d588 100644
--- a/source/ru/oauth.rst
+++ b/source/oauth.rst
@@ -1,24 +1,18 @@
Авторизация по протоколу OAuth2
-------------------------------
-На этой странице вы найдёте информацию о реализации авторизации по протоколу OAuth2 на вашем проекте через сервис
-Аккаунты Ely.by. Реализация этого протокола позволяет вашим пользователям производить авторизацию с использованием
-своего аккаунта Ely.by.
+На этой странице вы найдёте информацию о реализации авторизации по протоколу OAuth2 на вашем проекте через сервис Аккаунты Ely.by. Реализация этого протокола позволяет вашим пользователям производить авторизацию с использованием своего аккаунта Ely.by.
Регистрация приложения
======================
-Для начала вам необходимо `создать новое приложение `_. Выберите тип
-приложения **Веб‑сайт**. В качестве *адреса переадресации* можно указать только домен, но для повышения безопасности
-лучше использовать полный путь переадресации. Примеры допустимых адресов:
+Для начала вам необходимо `создать новое приложение `_. Выберите тип приложения **Веб‑сайт**. В качестве *адреса переадресации* можно указать только домен, но для повышения безопасности лучше использовать полный путь переадресации. Примеры допустимых адресов:
-* :samp:`http://site.com`
-* :samp:`http://site.com/oauth/ely`
-* :samp:`http://site.com/oauth.php?provider=ely`
+* ``http://site.com``
+* ``http://site.com/oauth/ely``
+* ``http://site.com/oauth.php?provider=ely``
-После успешного добавления приложения вы попадёте на страницу со списком всех ваших приложений. Кликнув по названию
-приложения вы увидите его идентификатор ``clientId`` и секрет ``clientSecret``. Они буду использоваться на
-следующих шагах.
+После успешного добавления приложения вы попадёте на страницу со списком всех ваших приложений. Кликнув по названию приложения вы увидите его идентификатор ``clientId`` и секрет ``clientSecret``. Они буду использоваться на следующих шагах.
Инициализация авторизации
=========================
@@ -37,34 +31,29 @@
- Пример значения
- Описание
* - *clientId*
- - :samp:`ely`
+ - ``ely``
- **Обязательное**. ClientId, полученный при регистрации.
* - *redirect_uri*
- - :samp:`http://site.com/oauth.php`
+ - ``http://site.com/oauth.php``
- **Обязательное**. Адрес обратной переадресации, совпадающий с адресом, указанным при регистрации приложения
* - *response_type*
- - :samp:`code`
+ - ``code``
- **Обязательное**. Тип ответа. На данный момент поддерживается только ``code``.
* - *scope*
- - :samp:`account_info account_email`
- - **Обязательное**. Перечень разрешений, доступ к которым вы хотите получить, разделённые пробелом. Смотрите все
- доступные права в `разделе ниже <#available-scopes>`_.
+ - ``account_info account_email``
+ - **Обязательное**. Перечень разрешений, доступ к которым вы хотите получить, разделённые пробелом. Смотрите все доступные права в `разделе ниже <#available-scopes>`_.
* - *state*
- - :samp:`isfvubuysdboinsbdfvit`
- - Случайно сгенерированная строка. Используется для увеличения безопасности в качестве идентификатора сессии. Будет
- возвращена в неизменённом виде после завершения авторизации.
+ - ``isfvubuysdboinsbdfvit``
+ - Случайно сгенерированная строка. Используется для увеличения безопасности в качестве идентификатора сессии. Будет возвращена в неизменённом виде после завершения авторизации.
* - *description*
- - :samp:`यो अनुप्रयोग विवरण`
- - Если ваше приложение доступно на нескольких языках, то используя это поле вы можете переопределить стандартное
- описание в соответствии с предпочтительным языком пользователя.
+ - ``यो अनुप्रयोग विवरण``
+ - Если ваше приложение доступно на нескольких языках, то используя это поле вы можете переопределить стандартное описание в соответствии с предпочтительным языком пользователя.
* - *prompt*
- - :samp:`consent` или :samp:`select_account`
- - Принудительно отобразить запрос прав (``consent``) или принудительно запросить выбор аккаунта
- (``select_account``).
+ - ``consent`` или ``select_account``
+ - Принудительно отобразить запрос прав (``consent``) или принудительно запросить выбор аккаунта (``select_account``).
* - *login_hint*
- - :samp:`erickskrauch` или :samp:`erickskrauch@ely.by`
- - Если у пользователя есть несколько аккаунтов, то указав этот в этом параметре username или email пользователя вы
- автоматически выберете аккаунт за него. Это полезно в случае повторного входа, когда токен истёк.
+ - ``erickskrauch`` или ``erickskrauch@ely.by``
+ - Если у пользователя есть несколько аккаунтов, то указав этот в этом параметре username или E-mail пользователя вы автоматически выберете аккаунт за него. Это полезно в случае повторного входа, когда токен истёк.
.. _available_scopes:
.. list-table:: Перечень доступных scopes
@@ -76,12 +65,11 @@
* - **account_email**
- В ответе на запрос информации о пользователе будет также присутствовать его email.
* - **offline_access**
- - Вместе с ``access_token`` вы также получите и ``refresh_token``. Смотрите подробнее
- `соответствующем разделе <#refresh-token-grant>`_.
+ - Вместе с ``access_token`` вы также получите и ``refresh_token``. Смотрите подробнее `соответствующем разделе <#refresh-token-grant>`_.
* - **minecraft_server_session**
- ``access_token`` можно будет использовать в качестве сессии для Minecraft.
-------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
Сформировав ссылку, разместите её в вашем шаблоне:
@@ -89,11 +77,9 @@
Войти через Ely.by
-По нажатию на ссылку, пользователь попадёт на нашу страницу авторизации, откуда после он будет перенаправлен обратно
-по адресу, указанному в параметре ``redirect_uri``.
+По нажатию на ссылку, пользователь попадёт на нашу страницу авторизации, откуда после он будет перенаправлен обратно по адресу, указанному в параметре ``redirect_uri``.
-Обратная переадресация выполняется в виде ``?code=<код авторизации>&state=`` для успешной
-авторизации и ``&error_message=<описание ошибки>`` для неудачной.
+Обратная переадресация выполняется в виде ``?code=<код авторизации>&state=`` для успешной авторизации и ``&error_message=<описание ошибки>`` для неудачной.
Пример успешного и неудачного редиректов:
@@ -107,8 +93,7 @@
Обмен кода на ключ
==================
-После получения кода авторизации (``auth_code``), вам необходимо обменять его на ключ авторизации (``access_key``).
-Для этого необходимо выполнить POST запрос на URL:
+После получения кода авторизации (``auth_code``), вам необходимо обменять его на ключ авторизации (``access_key``). Для этого необходимо выполнить POST запрос на URL:
.. code-block:: text
@@ -176,8 +161,7 @@
Ответ сервера
~~~~~~~~~~~~~
-В случае успешного запроса в теле ответа будет находиться результат обмена кода авторизации на ``access_token``.
-Данные являются JSON документом и могут быть легко интерпретированы средствами используемого языка программирования.
+В случае успешного запроса в теле ответа будет находиться результат обмена кода авторизации на ``access_token``. Данные являются JSON документом и могут быть легко интерпретированы средствами используемого языка программирования.
Тело JSON документа содержит следующие поля:
@@ -190,14 +174,12 @@
"expires_in": 86400 // Количество секунд, на которое выдан токен
}
-На этом процедура авторизации закончена. Полученный ``access_token`` может быть использован для получения информации о
-пользователе и взаимодействия с нашим API.
+На этом процедура авторизации закончена. Полученный ``access_token`` может быть использован для получения информации о пользователе и взаимодействия с нашим API.
Получение информации о пользователе
===================================
-Если полученный токен имеет scope ``account_info``, то вы можете запросить информацию об аккаунте пользователя. Для
-этого необходимо отправить запрос на URL:
+Если полученный токен имеет scope ``account_info``, то вы можете запросить информацию об аккаунте пользователя. Для этого необходимо отправить запрос на URL:
.. code-block:: text
@@ -235,23 +217,18 @@
"email": "erickskrauch@ely.by"
}
-Обратите внимание, что поле ``email`` будет присутствовать лишь в том случае, когда был запрошен scope
-``account_email``.
+Обратите внимание, что поле ``email`` будет присутствовать лишь в том случае, когда был запрошен scope ``account_email``.
-.. note:: В ходе дальнейшего развития сервиса, количество возвращаемых полей может увеличиться, но уже существующие
- останутся теми же.
+.. note:: В ходе дальнейшего развития сервиса, количество возвращаемых полей может увеличиться, но уже существующие останутся теми же.
.. _refresh-token-grant:
Обновление токена доступа
=========================
-Если при выполнении авторизации вами было запрошено право на получение scope ``offline_access``, то вместе с
-``access_token`` вы также получите и ``refresh_token``. Данный токен не истекает и может быть использован для получения
-нового токена доступа, когда он истечёт.
+Если при выполнении авторизации вами было запрошено право на получение scope ``offline_access``, то вместе с ``access_token`` вы также получите и ``refresh_token``. Данный токен не истекает и может быть использован для получения нового токена доступа, когда он истечёт.
-Для выполнения операции обновления токена необходимо отправить POST запрос на тот же URL, что использовался и
-`при обмене кода на ключ доступа <#authorization-code-grant>`_, но со следующими параметрами:
+Для выполнения операции обновления токена необходимо отправить POST запрос на тот же URL, что использовался и `при обмене кода на ключ доступа <#authorization-code-grant>`_, но со следующими параметрами:
.. list-table::
:widths: 1 99
@@ -262,8 +239,7 @@
* - ``client_secret``
- ClientSecret, полученный при регистрации приложения.
* - ``scope``
- - Те же scope, что были запрошены и при получении начального токена доступа. Попытка запросить большее количество
- прав приведёт к ошибке.
+ - Те же scope, что были запрошены и при получении начального токена доступа. Попытка запросить большее количество прав приведёт к ошибке.
* - ``refresh_token``
- Непосредственно токен, полученный вместе с начальным токеном доступа.
@@ -291,66 +267,56 @@
$result = json_decode(curl_exec($curl), true);
curl_close($curl);
-В качестве ответа будет точно такое же тело, какое было получено в результате
-`обмена кода на ключ доступа <#authorization-code-grant-response>`_. Поле ``refresh_token`` будет отсутствовать.
+В качестве ответа будет точно такое же тело, какое было получено в результате `обмена кода на ключ доступа <#authorization-code-grant-response>`_. Поле ``refresh_token`` будет отсутствовать.
Готовые библиотеки
==================
-Более простым способом будет использовать уже готовую библиотеку, которой будет необходимо передать лишь регистрационные
-параметры. Ниже перечислены библиотеки для различных языков программирования. Вы можете дополнить этот список своей
-библиотекой.
+Более простым способом будет использовать уже готовую библиотеку, которой будет необходимо передать лишь регистрационные параметры. Ниже перечислены библиотеки для различных языков программирования. Вы можете дополнить этот список своей библиотекой.
* **PHP**:
- - [Official] https://github.com/elyby/league-oauth2-provider
+ - [Официальная] https://github.com/elyby/league-oauth2-provider
* **Ruby**:
- - [Official] https://github.com/elyby/omniauth-ely
+ - [Официальная] https://github.com/elyby/omniauth-ely
Возможные ошибки
================
-Ниже приведены стандартные ошибки, которые вы можете получить в случае неправильной передачи данных на сервер
-авторизации. Если вы столкнулись с ошибкой, не описанной в этой документации, пожалуйста, сообщите о ней через
-`форму обратной связи `_.
+Ниже приведены стандартные ошибки, которые вы можете получить в случае неправильной передачи данных на сервер авторизации. Если вы столкнулись с ошибкой, не описанной в этой документации, пожалуйста, сообщите о ней через `форму обратной связи `_.
.. _auth-start-errors:
Ошибки при инициализации авторизации
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Этот раздел описывает ошибки, отображаемые при переадресации пользователя с вашего сайта на нашу страницу инициализации
-авторизации.
+Этот раздел описывает ошибки, отображаемые при переадресации пользователя с вашего сайта на нашу страницу инициализации авторизации.
.. code-block:: text
Invalid request ({parameter} required).
-Данная ошибка означает, что вы передали не все необходимые параметры. Чтобы решить эту ошибку просто добавьте
-недостающий параметр.
+Данная ошибка означает, что вы передали не все необходимые параметры. Чтобы решить эту ошибку просто добавьте недостающий параметр.
.. code-block:: text
Invalid response type '{invalid_response_type_value}'.
-Данная ошибка означает, что вы передали неподдерживаемый тип ``response_type``. На данный момент поддерживается только
-значение ``code``.
+Данная ошибка означает, что вы передали неподдерживаемый тип ``response_type``. На данный момент поддерживается только значение ``code``.
.. code-block:: text
Invalid scope '{invalid_scope}'.
-Ошибка указывает на то, что было запрошено неизвестный ``scope``. Убедитесь, что вы запрашиваете
-`поддерживаемые права <#available-scopes>`_.
+Ошибка указывает на то, что было запрошено неизвестный ``scope``. Убедитесь, что вы запрашиваете `поддерживаемые права <#available-scopes>`_.
.. code-block:: text
Can not find application you are trying to authorize.
-Данная ошибка говорит о том, что переданные параметры не соответствуют ни одному из зарегистрированных приложений.
-Для решения проблемы исправьте ваши значения ``client_id`` и ``redirect_uri``.
+Данная ошибка говорит о том, что переданные параметры не соответствуют ни одному из зарегистрированных приложений. Для решения проблемы исправьте ваши значения ``client_id`` и ``redirect_uri``.
.. _issue-token-errors:
@@ -366,8 +332,7 @@
"error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"code\" parameter."
}
-В поле ``error`` находится системный идентификатор ошибки, в ``error_description`` — описание ошибки на английском
-языке.
+В поле ``error`` находится системный идентификатор ошибки, в ``error_description`` — описание ошибки на английском языке.
**Возможные значения error:**
@@ -378,17 +343,14 @@
* - ``invalid_request``
- Переданы не все необходимые параметры запроса или значение ``code`` не был найден в базе выданных кодов.
* - ``unsupported_grant_type``
- - Данная ошибка сигнализирует о том, что вы попытались произвести авторизацию по неизвестному для нашего OAuth2
- сервера типу Grant.
+ - Данная ошибка сигнализирует о том, что вы попытались произвести авторизацию по неизвестному для нашего OAuth2 сервера типу Grant.
* - ``invalid_client``
- - Эта ошибка возникает в случае, когда трио значений ``client_id``, ``client_secret`` и ``redirect_uri`` не совпали
- ни с одним из зарегистрированных приложений.
+ - Эта ошибка возникает в случае, когда трио значений ``client_id``, ``client_secret`` и ``redirect_uri`` не совпали ни с одним из зарегистрированных приложений.
Ошибки при запросе информации о пользователе
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Ответ со статусом ``401`` указывает на то, что заголовок ``Authorization`` не присутствует в запросе или его значение
-сформировано неверно. Тело ответа будет следующим:
+Ответ со статусом ``401`` указывает на то, что заголовок ``Authorization`` не присутствует в запросе или его значение сформировано неверно. Тело ответа будет следующим:
.. code-block:: json
@@ -398,8 +360,7 @@
"message": "Your request was made with invalid credentials."
}
-Ответ со статусом ``403`` сигнализирует о том, что переданный в заголовке ``Authorization`` токен не содержит scope
-``account_info`` или он истёк. Получаемый ответ будет иметь следующий формат:
+Ответ со статусом ``403`` сигнализирует о том, что переданный в заголовке ``Authorization`` токен не содержит scope ``account_info`` или он истёк. Получаемый ответ будет иметь следующий формат:
.. code-block:: json
@@ -412,15 +373,13 @@
Ошибки при обновлении токена доступа
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-При выполнении обновления токена доступа вам могут встретиться те же ошибки, что и при
-`обмене кода на ключ доступа <#issue-token-errors>`_, а также несколько новых:
+При выполнении обновления токена доступа вам могут встретиться те же ошибки, что и при `обмене кода на ключ доступа <#issue-token-errors>`_, а также несколько новых:
.. list-table::
:widths: 1 99
:header-rows: 0
* - ``invalid_request``
- - Переданы не все необходимые параметры запроса или значение ``refresh_token`` не был найден в базе выданных
- токенов.
+ - Переданы не все необходимые параметры запроса или значение ``refresh_token`` не был найден в базе выданных токенов.
* - ``invalid_scope``
- Были перечислены неподдерживаемые scope или запрошено больше, чем было у изначального токена.
diff --git a/source/skin-system.html b/source/skin-system.html
deleted file mode 100644
index b1663e1..0000000
--- a/source/skin-system.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
diff --git a/source/ru/skins-system.rst b/source/skins-system.rst
similarity index 58%
rename from source/ru/skins-system.rst
rename to source/skins-system.rst
index 0dcb7ed..9570042 100644
--- a/source/ru/skins-system.rst
+++ b/source/skins-system.rst
@@ -1,46 +1,36 @@
Система скинов
--------------
-На этой странице вы найдёте информацию о доступных запросах к сервису системы скинов Ely.by. Вы можете использовать
-любой из них как дополнительный или основной источник скинов для своего проекта.
+На этой странице вы найдёте информацию о доступных запросах к сервису системы скинов Ely.by. Вы можете использовать любой из них как дополнительный или основной источник скинов для своего проекта.
-Сервис системы скинов Ely.by обеспечивает `проксирование текстур владельцев лицензии Minecraft <#textures-proxy>`_,
-что означает, что при использовании этого сервиса игроки будут видеть как скины премиум пользователей Minecraft,
-так и скины пользователей сервиса Ely.by.
+Сервис системы скинов Ely.by обеспечивает `проксирование текстур владельцев лицензии Minecraft <#textures-proxy>`_, что означает, что при использовании этого сервиса игроки будут видеть как скины премиум пользователей Minecraft, так и скины пользователей сервиса Ely.by.
-Мы стремимся соответствовать официальной системе скинов и не поддерживаем ушки и HD-скины. Система поддерживает плащи,
-но не позволяет игрокам самостоятельно их надевать.
+Мы стремимся соответствовать официальной системе скинов и не поддерживаем ушки и HD-скины. Система поддерживает плащи, но не позволяет игрокам самостоятельно их надевать.
-Если у вас есть предложения по развитию существующего функционала, пожалуйста,
-`создайте новый Issue `_ в
-`репозитории проекта Chrly `_.
+Если у вас есть предложения по развитию существующего функционала, пожалуйста, `создайте новый Issue `_ в `репозитории проекта Chrly `_.
-.. note:: Вы можете найти более подробную информацию о реализации сервера системы скинов в
- `репозитории проекта Chrly `_.
+.. note:: Вы можете найти более подробную информацию о реализации сервера системы скинов в `репозитории проекта Chrly `_.
URL-адреса запросов
===================
-Система скинов размещена на домене :samp:`http://skinsystem.ely.by`.
+Система скинов размещена на домене ``http://skinsystem.ely.by``.
-Во всех запросах параметр :samp:`nickname` должен быть заменён на ник игрока. Значение не чувствительно к регистру.
+Во всех запросах параметр ``nickname`` должен быть заменён на ник игрока. Значение не чувствительно к регистру.
.. _skin-request:
.. function:: /skins/{nickname}.png
- URL для загрузки текстуры скина. Расширение :samp:`.png` опционально. Если текстура не будет найдена,
- сервер вернёт ответ с :samp:`404` статусом.
+ URL для загрузки текстуры скина. Расширение ``.png`` опционально. Если текстура не будет найдена, сервер вернёт ответ с ``404`` статусом.
.. _cape-request:
.. function:: /cloaks/{nickname}.png
- URL для загрузки текстуры плаща. Расширение :samp:`.png` опционально. Если текстура не будет найдена,
- сервер вернёт ответ с :samp:`404` статусом.
+ URL для загрузки текстуры плаща. Расширение ``.png`` опционально. Если текстура не будет найдена, сервер вернёт ответ с ``404`` статусом.
.. function:: /textures/{nickname}
- По этому URL вы можете получить текстуры в формате, указанному в поле :samp:`textures` одноимённого property
- в `ответе на запрос подписанных текстур `_:
+ По этому URL вы можете получить текстуры в формате, указанному в поле ``textures`` одноимённого property в `ответе на запрос подписанных текстур `_:
.. code-block:: javascript
@@ -56,21 +46,15 @@ URL-адреса запросов
}
}
- В зависимости от доступных игроку текстур могут отсутствовать поля :samp:`SKIN` или :samp:`CAPE`.
- Если модель скина не является :samp:`slim`, то поле :samp:`metadata` также будет отсутствовать.
+ В зависимости от доступных игроку текстур могут отсутствовать поля ``SKIN`` или ``CAPE``. Если модель скина не является ``slim``, то поле ``metadata`` также будет отсутствовать.
- Если текстуры не будут найдены, сервер вернёт пустой ответ с :samp:`204` статусом.
+ Если текстуры не будут найдены, сервер вернёт пустой ответ с ``204`` статусом.
.. function:: /profile/{nickname}
- Данный запрос является аналогом запроса
- `профиля игрока в API Mojang `_, только вместо
- идентификации пользователя по UUID используется его ник. Также, как и в API Mojang, вы можете добавить к запросу
- ``?unsigned=false``, чтобы получить текстуры с подписью. В ответе также будет присутствовать дополнительное property
- с ``name`` равным **ely**.
+ Данный запрос является аналогом запроса `профиля игрока в API Mojang `_, только вместо идентификации пользователя по UUID используется его ник. Также, как и в API Mojang, вы можете добавить к запросу ``?unsigned=false``, чтобы получить текстуры с подписью. В ответе также будет присутствовать дополнительное property с ``name`` равным **ely**.
- Если у пользователя нет текстур, то они будут запрошены через прокси Mojang, после чего переподписаны с
- использованием `нашего ключа подписи <#signature-verification-key-request>`_.
+ Если у пользователя нет текстур, то они будут запрошены через прокси Mojang, после чего переподписаны с использованием `нашего ключа подписи <#signature-verification-key-request>`_.
.. code-block:: javascript
@@ -90,15 +74,12 @@ URL-адреса запросов
]
}
- Если запрошенный никнейм не будет найден ни в локальном хранилище, ни у Mojang, сервер вернёт пустой ответ с ``204``
- статусом.
+ Если запрошенный никнейм не будет найден ни в локальном хранилище, ни у Mojang, сервер вернёт пустой ответ с ``204`` статусом.
.. _signature-verification-key-request:
.. function:: /signature-verification-key.der
- Данный запрос возвращает публичный ключ, который может быть использован для проверки подписи текстур. Ключ
- предоставляется в формате ``DER``. Этот формат используется внутри Authlib, поэтому ключ может быть в ней использован
- без модификации алгоритма проверки подписи.
+ Данный запрос возвращает публичный ключ, который может быть использован для проверки подписи текстур. Ключ предоставляется в формате ``DER``. Этот формат используется внутри Authlib, поэтому ключ может быть в ней использован без модификации алгоритма проверки подписи.
.. function:: /signature-verification-key.pem
@@ -106,10 +87,7 @@ URL-адреса запросов
.. function:: /textures/signed/{nickname}
- Этот запрос используется в нашем `плагине серверной системы скинов `_ для
- загрузки текстур с оригинальной подписью Mojang. Полученные в ответе текстуры могут быть без изменений переданы в
- немодифицированный игровой клиент. В ответе также будет присутствовать дополнительное property с :samp:`name`
- равным **ely**.
+ Этот запрос используется в нашем `плагине серверной системы скинов `_ для загрузки текстур с оригинальной подписью Mojang. Полученные в ответе текстуры могут быть без изменений переданы в немодифицированный игровой клиент. В ответе также будет присутствовать дополнительное property с ``name`` равным **ely**.
.. code-block:: javascript
@@ -129,23 +107,19 @@ URL-адреса запросов
]
}
- По умолчанию для этого запроса не применяется проксирование текстур. Чтобы его включить, добавьте дополнительный
- GET параметр :samp:`?proxy=true`.
+ По умолчанию для этого запроса не применяется проксирование текстур. Чтобы его включить, добавьте дополнительный GET параметр ``?proxy=true``.
- Если текстуры не будут найдены, сервер вернёт пустой ответ с :samp:`204` статусом.
+ Если текстуры не будут найдены, сервер вернёт пустой ответ с ``204`` статусом.
-------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
-При совершении любого из вышеописанных запросов вы также можете передать ряд дополнительных GET параметров. Они будут
-использованы для анализа использования сервиса разными версиями игры.
+При совершении любого из вышеописанных запросов вы также можете передать ряд дополнительных GET параметров. Они будут использованы для анализа использования сервиса разными версиями игры.
-:version: Версия протокола, по которому идёт запрос на скины. На данный момент это версия :samp:`2` ,
- т.е. вам необходимо указать :samp:`version=2`.
+:version: Версия протокола, по которому идёт запрос на скины. На данный момент это версия ``2`` , т.е. вам необходимо указать ``version=2``.
:minecraft_version: Версия Minecraft, с которой идёт запрос.
-:authlib_version: Версия используемой Authlib. Этот параметр актуален для версий Minecraft 1.7.6+, где
- для загрузки скинов стала использоваться отдельная библиотека, а не внутриигровой код.
+:authlib_version: Версия используемой Authlib. Этот параметр актуален для версий Minecraft 1.7.6+, где для загрузки скинов стала использоваться отдельная библиотека, а не внутриигровой код.
Пример запроса текстур с передачей вышеописанных параметров:
@@ -156,10 +130,7 @@ URL-адреса запросов
Вспомогательные URL
+++++++++++++++++++
-Также запрос скина и плаща можно выполнить, передавая ник через GET параметр. Эта возможность используется для
-передачи аналитических параметров в версиях игры до 1.5.2, когда ник просто добавлялся в конец строки. Для этого вся
-строка выстраивается таким образом, чтобы последним параметром шёл :samp:`name`, после добавления ника к которому
-получался полный запрос на текстуру.
+Также запрос скина и плаща можно выполнить, передавая ник через GET параметр. Эта возможность используется для передачи аналитических параметров в версиях игры до 1.5.2, когда ник просто добавлялся в конец строки. Для этого вся строка выстраивается таким образом, чтобы последним параметром шёл ``name``, после добавления ника к которому получался полный запрос на текстуру.
.. function:: /skins?name={nickname}.png
@@ -181,23 +152,17 @@ URL-адреса запросов
Проксирование текстур
=====================
-Сервис системы скинов Ely.by получает текстуры из официальной системы скинов в случае, если в базе данных не было
-найдено информации о текстурах для запрошенного имени пользователя. Также запрос будет проксирован, если запись о скине
-будет найдена, но он будет стандартным.
+Сервис системы скинов Ely.by получает текстуры из официальной системы скинов в случае, если в базе данных не было найдено информации о текстурах для запрошенного имени пользователя. Также запрос будет проксирован, если запись о скине будет найдена, но он будет стандартным.
Для улучшения пропускной способности проксирующего алгоритма, информация о текстурах кешируется в 2 стадии:
-* Соответствие ника и UUID хранится в
- `течение 30 дней `_.
+* Соответствие ника и UUID хранится в `течение 30 дней `_.
-* Информация о текстурах обновляется не чаще
- `раза в минуту `_.
+* Информация о текстурах обновляется не чаще `раза в минуту `_.
-Если вы владеете лицензионным аккаунтом Minecraft, но ваш ник занят, пожалуйста, обратитесь в
-`службу поддержки `_ и после небольшой проверки мы передадим ник в ваше пользование.
+Если вы владеете лицензионным аккаунтом Minecraft, но ваш ник занят, пожалуйста, обратитесь в `службу поддержки `_ и после небольшой проверки мы передадим ник в ваше пользование.
Готовые реализации
==================
-Готовые реализации патчей и инструкции по их установке могут быть найдены в
-`разделе загрузок на главном сайте Ely.by `_.
+Готовые реализации патчей и инструкции по их установке могут быть найдены в `разделе загрузок на главном сайте Ely.by `_.