Merge branch 'upgrade'

This commit is contained in:
ErickSkrauch 2021-03-23 14:38:44 +01:00
commit ccc10f1850
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
33 changed files with 1058 additions and 1068 deletions

84
.github/workflows/ci.yml vendored Normal file
View File

@ -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

3
.gitignore vendored
View File

@ -6,3 +6,6 @@
### venv folder
/venv
### The directory for the locales from the Crowdin
/locale

0
.nojekyll Normal file
View File

View File

@ -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

16
Pipfile Normal file
View File

@ -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"

626
Pipfile.lock generated Normal file
View File

@ -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"
}
}
}

View File

@ -1,5 +0,0 @@
include:
- _downloads
- _images
- _sources
- _static

169
build-multilang.py Executable file
View File

@ -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")

8
crowdin.yml Normal file
View File

@ -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

View File

@ -1,3 +0,0 @@
Sphinx~=3.0.0
sphinx-rtd-theme~=0.4.3
sphinx-autobuild~=0.7.1

View File

@ -17,4 +17,3 @@
</script>
{% endblock %}

View File

@ -1,8 +0,0 @@
<html lang="en">
<head>
<meta http-equiv="refresh" content="0; URL=/ru/api.html" />
<script>
window.location.href = '/ru/api.html'
</script>
</head>
</html>

View File

@ -1,38 +1,25 @@
Ely.by API (симуляция Mojang API)
---------------------------------
Здесь приведена информация об API, совместимом с функционалом `Mojang Api <http://wiki.vg/Mojang_API>`_. Обращаем ваше
внимание на то, что это не полноценное API Ely.by, а только набор дополнительных запросов, реализованных на базе нашего
`сервера авторизации </minecraft-auth.html>`_.
Заметки
=======
* API не имеет ограничения на количество запросов. У нас есть просто настроенный fail2ban, который будет банить особо
надоедливых клиентов. Такие дела.
Здесь приведена информация об API, совместимом с функционалом `Mojang Api <http://wiki.vg/Mojang_API>`_. Обращаем ваше внимание на то, что это не полноценное API Ely.by, а только набор дополнительных запросов, реализованных на базе нашего :doc:`сервера авторизации <minecraft-auth>`.
Запросы
=======
В этой секции будут описаны запросы и их же варианты для 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
См. `запрос профиля для сервера авторизации <minecraft-auth.html#profile-request>`_.
См. :ref:`запрос профиля для сервера авторизации <profile-request>`.
Возможные ошибки
================
@ -137,11 +118,9 @@ IllegalArgumentException
.. code-block:: javascript
// Пример ошибки при неправильном формате UUID
{
"error": "IllegalArgumentException",
"errorMessage": "Invalid uuid format."
}
``errorMessage`` не всегда совпадает с таковым у Mojang, но в основном это касается только специфичных только для Ely
ошибок. Оригинальные же запросы и ожидаемые от них ошибки повторяют тексты Mojang.
``errorMessage`` не всегда совпадает с таковым у Mojang, но в основном это касается только специфичных только для Ely ошибок. Оригинальные же запросы и ожидаемые от них ошибки повторяют тексты Mojang.

View File

@ -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: Сообщение при запуске сервера

View File

@ -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)

View File

@ -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 <https://github.com/yushijinhun/authlib-injector/releases/latest>`_.
Here is the documentation of the key aspects of installing and using the library. For more information, see the `original documentation in Chinese <https://github.com/yushijinhun/authlib-injector/wiki>`_.
.. _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 <https://github.com/yushijinhun/authlib-injector/releases/latest>`_ 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 <https://github.com/Codex-in-somnio/LaunchHelper/releases/latest>`_.
#. 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.

View File

@ -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 <https://account.ely.by/dev/applications/new>`_. 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=<clientId>&redirect_uri=<redirectUri>&response_type=code&scope=<scopesList>
.. 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
<a href="<your_link>">Login via Ely.by</a>
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 ``<redirect_uri>?code=<auth_code>&state=<state>`` for a successful authorization and
``<redirect_uri?error=<error_identifier>&error_message=<error_description>`` 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
<?php
// This variable will store your OAuth2 settings
$oauthParams = [
'client_id' => '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
<?php
$accessToken = 'some_access_token_value';
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'https://account.ely.by/api/account/v1/info');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $accessToken,
]);
$result = json_decode(curl_exec($curl), true);
curl_close($curl);
In response, you will receive a JSON document with the following contents:
.. code-block:: json
{
"id": 1,
"uuid": "ffc8fdc9-5824-509e-8a57-c99b940fb996",
"username": "ErickSkrauch",
"registeredAt": 1470566470,
"profileLink": "http:\/\/ely.by\/u1",
"preferredLanguage": "be",
"email": "erickskrauch@ely.by"
}
Note that the ``email`` field will only be present when the ``account_email`` scope has been requested.
.. note:: In the future, the number of returned fields may increase, but existing ones will remain the same.
.. _refresh-token-grant:
Refreshing access token
=======================
If you have requested the scope ``offline_access`` during authorization, then along with your ``access_token`` you'll
also get ``refresh_token``. This token doesn't expire and can be used to obtain a new access token when that one
expires.
To perform a token update, you have to send a POST request to the same URL that was used for
`exchanging the auth code for an access token <#authorization-code-grant>`_, 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
<?php
// refresh_token that was receive after an authorization
$refreshToken = 'm0APA99Ee7E6jNryVBrZ4qlktsEiwgspKEAotaze';
$requestParams = [
'client_id' => '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 <http://ely.by/site/contact>`_.
.. _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.

View File

@ -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 <https://github.com/elyby/chrly/issues/new>`_ at the
`Chrly project repository <https://github.com/elyby/chrly>`_.
.. note:: You can find more detailed information about the implementation of the skins system server in the
`Chrly project repository <https://github.com/elyby/chrly>`_.
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 <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape>`_.
.. 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 <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape>`_, 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 <http://ely.by/server-skins-system>`_ 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 <https://help.minecraft.net/hc/en-us/articles/360034636712-Minecraft-Usernames#article-container:~:text=How%20often%20can%20I%20change%20my%20username%3F>`_.
* Information about textures isn't updated more often than
`once a minute <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape:~:text=You%20can%20request%20the%20same%20profile%20once%20per%20minute>`_.
If you own a Minecraft premium account, but your nickname is busy, please contact our
`support team <http://ely.by/site/contact>`_ 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 <http://ely.by/load>`_.

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -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 <https://github.com/elyby/docs>`_.
Вы можете свободно улучшать и вносить предложения по изменениям в документацию в `репозитории проекта <https://github.com/elyby/docs>`_.
.. toctree::
:caption: English articles:
:maxdepth: 2
:glob:
en/*
.. toctree::
:caption: Статьи на русском:
:maxdepth: 2
:glob:
ru/*
*

View File

@ -1,8 +0,0 @@
<html lang="en">
<head>
<meta http-equiv="refresh" content="0; URL=/ru/minecraft-auth.html" />
<script>
window.location.href = '/ru/minecraft-auth.html'
</script>
</head>
</html>

View File

@ -3,8 +3,7 @@
Здесь приведена информация по авторизации для лаунчеров и серверов Minecraft через сервис авторизации Ely.by.
Протокол авторизации реализован максимально похожим на `оригинальный протокол авторизации Mojang <http://wiki.vg/Authentication>`_,
но тем не менее эта документация описывает все доступные функции конкретно сервиса авторизации Ely.by.
Протокол авторизации реализован максимально похожим на `оригинальный протокол авторизации Mojang <http://wiki.vg/Authentication>`_, но тем не менее эта документация описывает все доступные функции конкретно сервиса авторизации 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:`соответствующий раздел документации <authlib-injector>`.
.. attention:: Ely.by поддерживает библиотеку authlib-injector. Это наиболее простой и универсальный способ установки системы авторизации в игру и игровые сервера. За подробностями обратитесь в :doc:`соответствующий раздел документации <authlib-injector>`.
Поскольку самостоятельная реализация связана с трудностями поиска исходников, подключения зависимостей и в конце-концов
с процессом компиляции, на `странице загрузок нашей системы скинов <//ely.by/load>`_ вы можете загрузить уже
готовые библиотеки со всеми необходимыми изменениями. Выберите в выпадающем списке необходимую версию и следуйте
инструкции по установке, размещённой на той же странице ниже.
Поскольку самостоятельная реализация связана с трудностями поиска исходников, подключения зависимостей и в конце-концов с процессом компиляции, на `странице загрузок нашей системы скинов <https://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 <files/authlib/authlib-1.3.1.jar>`
* Minecraft 1.7.2 - :download:`authlib 1.3 <../_static/minecraft-auth/authlib/authlib-1.3.jar>`
* Minecraft 1.7.2 - :download:`authlib 1.3 <files/authlib/authlib-1.3.jar>`
Для установки вам необходимо заменить оригинальную библиотеку, располагающуюся по пути
``<директория установки minecraft>/libraries/com/mojang/authlib/``. Убедитесь в том, что версии скачанного и заменяемого
файлов совпадают.
Для установки вам необходимо заменить оригинальную библиотеку, располагающуюся по пути ``<директория установки minecraft>/libraries/com/mojang/authlib/``. Убедитесь в том, что версии скачанного и заменяемого файлов совпадают.
.. _install-server:
Установка authlib на сервер
===========================
Сервер также использует authlib для выполнения авторизации игрока, поэтому соответствующие изменения должны быть
также применены и к нему. Ниже приведены инструкции по установки authlib для различных реализаций сервера Minecraft.
Сервер также использует authlib для выполнения авторизации игрока, поэтому соответствующие изменения должны быть также применены и к нему. Ниже приведены инструкции по установки authlib для различных реализаций сервера Minecraft.
.. note:: Если ни одна из приведённых ниже инструкций не подошла для вашей реализации сервера, пожалуйста,
создайте `новый issue <https://github.com/elyby/docs/issues/new>`_ и мы допишем инструкцию для вашего сервера.
.. note:: Если ни одна из приведённых ниже инструкций не подошла для вашей реализации сервера, пожалуйста, создайте `новый issue <https://github.com/elyby/docs/issues/new>`_ и мы допишем инструкцию для вашего сервера.
.. _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 <https://repo1.maven.org/maven2/commons-io/commons-io/2.5/commons-io-2.5.jar>`_ и
`commons-lang3 <https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar>`_,
после чего аналогичным с authlib образом последовательно переместите содержимое скачанных архивов в файлы сервера.
Сперва выполните установку, как она описана для `оригинального сервера <#vanilla>`_. Затем скачайте библиотеки `commons-io <https://repo1.maven.org/maven2/commons-io/commons-io/2.5/commons-io-2.5.jar>`_ и `commons-lang3 <https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar>`_, после чего аналогичным с 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.

View File

@ -1,8 +0,0 @@
<html lang="en">
<head>
<meta http-equiv="refresh" content="0; URL=/ru/oauth.html" />
<script>
window.location.href = '/ru/oauth.html'
</script>
</head>
</html>

View File

@ -1,24 +1,18 @@
Авторизация по протоколу OAuth2
-------------------------------
На этой странице вы найдёте информацию о реализации авторизации по протоколу OAuth2 на вашем проекте через сервис
Аккаунты Ely.by. Реализация этого протокола позволяет вашим пользователям производить авторизацию с использованием
своего аккаунта Ely.by.
На этой странице вы найдёте информацию о реализации авторизации по протоколу OAuth2 на вашем проекте через сервис Аккаунты Ely.by. Реализация этого протокола позволяет вашим пользователям производить авторизацию с использованием своего аккаунта Ely.by.
Регистрация приложения
======================
Для начала вам необходимо `создать новое приложение <https://account.ely.by/dev/applications/new>`_. Выберите тип
приложения **Веб‑сайт**. В качестве *адреса переадресации* можно указать только домен, но для повышения безопасности
лучше использовать полный путь переадресации. Примеры допустимых адресов:
Для начала вам необходимо `создать новое приложение <https://account.ely.by/dev/applications/new>`_. Выберите тип приложения **Веб‑сайт**. В качестве *адреса переадресации* можно указать только домен, но для повышения безопасности лучше использовать полный путь переадресации. Примеры допустимых адресов:
* :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 @@
<a href="<ваша_ссылка>">Войти через Ely.by</a>
По нажатию на ссылку, пользователь попадёт на нашу страницу авторизации, откуда после он будет перенаправлен обратно
по адресу, указанному в параметре ``redirect_uri``.
По нажатию на ссылку, пользователь попадёт на нашу страницу авторизации, откуда после он будет перенаправлен обратно по адресу, указанному в параметре ``redirect_uri``.
Обратная переадресация выполняется в виде ``<redirect_uri>?code=<код авторизации>&state=<state>`` для успешной
авторизации и ``<redirect_uri?error=<идентификатор ошибки>&error_message=<описание ошибки>`` для неудачной.
Обратная переадресация выполняется в виде ``<redirect_uri>?code=<код авторизации>&state=<state>`` для успешной авторизации и ``<redirect_uri?error=<идентификатор ошибки>&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
Возможные ошибки
================
Ниже приведены стандартные ошибки, которые вы можете получить в случае неправильной передачи данных на сервер
авторизации. Если вы столкнулись с ошибкой, не описанной в этой документации, пожалуйста, сообщите о ней через
`форму обратной связи <http://ely.by/site/contact>`_.
Ниже приведены стандартные ошибки, которые вы можете получить в случае неправильной передачи данных на сервер авторизации. Если вы столкнулись с ошибкой, не описанной в этой документации, пожалуйста, сообщите о ней через `форму обратной связи <https://ely.by/site/contact>`_.
.. _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 или запрошено больше, чем было у изначального токена.

View File

@ -1,8 +0,0 @@
<html lang="en">
<head>
<meta http-equiv="refresh" content="0; URL=/ru/skins-system.html" />
<script>
window.location.href = '/ru/skins-system.html'
</script>
</head>
</html>

View File

@ -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 <https://github.com/elyby/chrly/issues/new>`_ в
`репозитории проекта Chrly <https://github.com/elyby/chrly>`_.
Если у вас есть предложения по развитию существующего функционала, пожалуйста, `создайте новый Issue <https://github.com/elyby/chrly/issues/new>`_ в `репозитории проекта Chrly <https://github.com/elyby/chrly>`_.
.. note:: Вы можете найти более подробную информацию о реализации сервера системы скинов в
`репозитории проекта Chrly <https://github.com/elyby/chrly>`_.
.. note:: Вы можете найти более подробную информацию о реализации сервера системы скинов в `репозитории проекта Chrly <https://github.com/elyby/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
в `ответе на запрос подписанных текстур <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape>`_:
По этому URL вы можете получить текстуры в формате, указанному в поле ``textures`` одноимённого property в `ответе на запрос подписанных текстур <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape>`_:
.. 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 <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape>`_, только вместо
идентификации пользователя по UUID используется его ник. Также, как и в API Mojang, вы можете добавить к запросу
``?unsigned=false``, чтобы получить текстуры с подписью. В ответе также будет присутствовать дополнительное property
с ``name`` равным **ely**.
Данный запрос является аналогом запроса `профиля игрока в API Mojang <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape>`_, только вместо идентификации пользователя по 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}
Этот запрос используется в нашем `плагине серверной системы скинов <http://ely.by/server-skins-system>`_ для
загрузки текстур с оригинальной подписью Mojang. Полученные в ответе текстуры могут быть без изменений переданы в
немодифицированный игровой клиент. В ответе также будет присутствовать дополнительное property с :samp:`name`
равным **ely**.
Этот запрос используется в нашем `плагине серверной системы скинов <https://ely.by/server-skins-system>`_ для загрузки текстур с оригинальной подписью 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 дней <https://help.minecraft.net/hc/en-us/articles/360034636712-Minecraft-Usernames#article-container:~:text=How%20often%20can%20I%20change%20my%20username%3F>`_.
* Соответствие ника и UUID хранится в `течение 30 дней <https://help.minecraft.net/hc/en-us/articles/360034636712-Minecraft-Usernames#article-container:~:text=How%20often%20can%20I%20change%20my%20username%3F>`_.
* Информация о текстурах обновляется не чаще
`раза в минуту <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape:~:text=You%20can%20request%20the%20same%20profile%20once%20per%20minute>`_.
* Информация о текстурах обновляется не чаще `раза в минуту <https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape:~:text=You%20can%20request%20the%20same%20profile%20once%20per%20minute>`_.
Если вы владеете лицензионным аккаунтом Minecraft, но ваш ник занят, пожалуйста, обратитесь в
`службу поддержки <http://ely.by/site/contact>`_ и после небольшой проверки мы передадим ник в ваше пользование.
Если вы владеете лицензионным аккаунтом Minecraft, но ваш ник занят, пожалуйста, обратитесь в `службу поддержки <https://ely.by/site/contact>`_ и после небольшой проверки мы передадим ник в ваше пользование.
Готовые реализации
==================
Готовые реализации патчей и инструкции по их установке могут быть найдены в
`разделе загрузок на главном сайте Ely.by <http://ely.by/load>`_.
Готовые реализации патчей и инструкции по их установке могут быть найдены в `разделе загрузок на главном сайте Ely.by <https://ely.by/load>`_.