PEP8, mkdocs, corrected logo

This commit is contained in:
DarkCat09 2022-06-23 15:13:56 +04:00
parent a75d729e93
commit 9c4274284f
99 changed files with 1625 additions and 39720 deletions

View file

@ -1,15 +0,0 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
build:
os: ubuntu-20.04
tools:
python: "3.10"
sphinx:
builder: html
configuration: docs/source/conf.py
fail_on_warning: false

View file

@ -1,4 +1,4 @@
![Python-Aternos Logo](https://i.ibb.co/60SRKcH/aternos-400.png) ![Python-Aternos Logo](https://i.ibb.co/3RXcXJ1/aternos-400.png)
*** ***
# Python Aternos # Python Aternos
An unofficial Aternos API written in Python. An unofficial Aternos API written in Python.

View file

@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View file

@ -1,4 +0,0 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 248bf4413f52c61df59cba2c064f51e2
tags: 645f666f9bcd5a90fca523b33c5a78b7

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,20 +0,0 @@
.. python-aternos documentation master file, created by
sphinx-quickstart on Fri Jun 17 14:02:03 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to python-aternos's documentation!
==========================================
.. toctree::
:maxdepth: 2
:caption: Contents:
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -1,8 +0,0 @@
python-aternos
==============
.. toctree::
:maxdepth: 4
python_aternos
setup

View file

@ -1,93 +0,0 @@
python\_aternos package
=======================
Submodules
----------
python\_aternos.atclient module
-------------------------------
.. automodule:: python_aternos.atclient
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atconf module
-----------------------------
.. automodule:: python_aternos.atconf
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atconnect module
--------------------------------
.. automodule:: python_aternos.atconnect
:members:
:undoc-members:
:show-inheritance:
python\_aternos.aterrors module
-------------------------------
.. automodule:: python_aternos.aterrors
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atfile module
-----------------------------
.. automodule:: python_aternos.atfile
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atfm module
---------------------------
.. automodule:: python_aternos.atfm
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atjsparse module
--------------------------------
.. automodule:: python_aternos.atjsparse
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atplayers module
--------------------------------
.. automodule:: python_aternos.atplayers
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atserver module
-------------------------------
.. automodule:: python_aternos.atserver
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atwss module
----------------------------
.. automodule:: python_aternos.atwss
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: python_aternos
:members:
:undoc-members:
:show-inheritance:

View file

@ -1,7 +0,0 @@
setup module
============
.. automodule:: setup
:members:
:undoc-members:
:show-inheritance:

View file

@ -1,134 +0,0 @@
/*
* _sphinx_javascript_frameworks_compat.js
* ~~~~~~~~~~
*
* Compatability shim for jQuery and underscores.js.
*
* WILL BE REMOVED IN Sphinx 6.0
* xref RemovedInSphinx60Warning
*
*/
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}

View file

@ -1,701 +0,0 @@
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Georgia, serif;
font-size: 17px;
background-color: #fff;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: 940px;
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 220px;
}
div.sphinxsidebar {
width: 220px;
font-size: 14px;
line-height: 1.5;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #fff;
color: #3E4349;
padding: 0 30px 0 30px;
}
div.body > .section {
text-align: left;
}
div.footer {
width: 940px;
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
p.caption {
font-family: inherit;
font-size: inherit;
}
div.relations {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0;
margin: -10px 0 0 0px;
text-align: center;
}
div.sphinxsidebarwrapper h1.logo {
margin-top: -10px;
text-align: center;
margin-bottom: 5px;
text-align: left;
}
div.sphinxsidebarwrapper h1.logo-name {
margin-top: 0px;
}
div.sphinxsidebarwrapper p.blurb {
margin-top: 0;
font-style: normal;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Georgia, serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar ul li.toctree-l1 > a {
font-size: 120%;
}
div.sphinxsidebar ul li.toctree-l2 > a {
font-size: 110%;
}
div.sphinxsidebar input {
border: 1px solid #CCC;
font-family: Georgia, serif;
font-size: 1em;
}
div.sphinxsidebar hr {
border: none;
height: 1px;
color: #AAA;
background: #AAA;
text-align: left;
margin-left: 0;
width: 50%;
}
div.sphinxsidebar .badge {
border-bottom: none;
}
div.sphinxsidebar .badge:hover {
border-bottom: none;
}
/* To address an issue with donation coming after search */
div.sphinxsidebar h3.donation {
margin-top: 10px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: Georgia, serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #DDD;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #EAEAEA;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
margin: 20px 0px;
padding: 10px 30px;
background-color: #EEE;
border: 1px solid #CCC;
}
div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fafafa;
}
div.admonition p.admonition-title {
font-family: Georgia, serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: #fff;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.warning {
background-color: #FCC;
border: 1px solid #FAA;
}
div.danger {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.error {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.caution {
background-color: #FCC;
border: 1px solid #FAA;
}
div.attention {
background-color: #FCC;
border: 1px solid #FAA;
}
div.important {
background-color: #EEE;
border: 1px solid #CCC;
}
div.note {
background-color: #EEE;
border: 1px solid #CCC;
}
div.tip {
background-color: #EEE;
border: 1px solid #CCC;
}
div.hint {
background-color: #EEE;
border: 1px solid #CCC;
}
div.seealso {
background-color: #EEE;
border: 1px solid #CCC;
}
div.topic {
background-color: #EEE;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt, code {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
.hll {
background-color: #FFC;
margin: 0 -12px;
padding: 0 12px;
display: block;
}
img.screenshot {
}
tt.descname, tt.descclassname, code.descname, code.descclassname {
font-size: 0.95em;
}
tt.descname, code.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #EEE;
background: #FDFDFD;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.field-list p {
margin-bottom: 0.8em;
}
/* Cloned from
* https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68
*/
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
table.footnote td.label {
width: .1px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
/* Matches the 30px from the narrow-screen "li > ul" selector below */
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #EEE;
padding: 7px 30px;
margin: 15px 0px;
line-height: 1.3em;
}
div.viewcode-block:target {
background: #ffd;
}
dl pre, blockquote pre, li pre {
margin-left: 0;
padding-left: 30px;
}
tt, code {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, code.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fff;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
/* Don't put an underline on images */
a.image-reference, a.image-reference:hover {
border-bottom: none;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt, a:hover code {
background: #EEE;
}
@media screen and (max-width: 870px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100%;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
li > ul {
/* Matches the 30px from the "ul, ol" selector above */
margin-left: 30px;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
@media screen and (max-width: 875px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: #fff;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: #FFF;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: #fff;
}
div.sphinxsidebar a {
color: #AAA;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
/* misc. */
.revsys-inline {
display: none!important;
}
/* Make nested-list/multi-paragraph items look better in Releases changelog
* pages. Without this, docutils' magical list fuckery causes inconsistent
* formatting between different release sub-lists.
*/
div#changelog > div.section > ul > li > p:only-child {
margin-bottom: 0;
}
/* Hide fugly table cell borders in ..bibliography:: directive output */
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
border: none;
/* Below needed in some edge cases; if not applied, bottom shadows appear */
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
/* relbar */
.related {
line-height: 30px;
width: 100%;
font-size: 0.9rem;
}
.related.top {
border-bottom: 1px solid #EEE;
margin-bottom: 20px;
}
.related.bottom {
border-top: 1px solid #EEE;
}
.related ul {
padding: 0;
margin: 0;
list-style: none;
}
.related li {
display: inline;
}
nav#rellinks {
float: right;
}
nav#rellinks li+li:before {
content: "|";
}
nav#breadcrumbs li+li:before {
content: "\00BB";
}
/* Hide certain items when printing */
@media print {
div.related {
display: none;
}
}

View file

@ -1,930 +0,0 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
/* Docutils 0.17 and older (footnotes & citations) */
dl.footnote > dt,
dl.citation > dt {
float: left;
margin-right: 0.5em;
}
dl.footnote > dd,
dl.citation > dd {
margin-bottom: 0em;
}
dl.footnote > dd:after,
dl.citation > dd:after {
content: "";
clear: both;
}
/* Docutils 0.18+ (footnotes & citations) */
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
/* Footnotes & citations ends */
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dt:after {
content: ":";
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

View file

@ -1 +0,0 @@
/* This file intentionally left blank. */

View file

@ -1,264 +0,0 @@
/*
* doctools.js
* ~~~~~~~~~~~
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0 &&
!parent.classList.contains(className) &&
!parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
parent.insertBefore(
span,
parent.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target)
);
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.highlightSearchWords();
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* highlight the search words provided in the url in the text
*/
highlightSearchWords: () => {
const highlight =
new URLSearchParams(window.location.search).get("highlight") || "";
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:Documentation.hideSearchWords()">' +
Documentation.gettext("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
const url = new URL(window.location);
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
const blacklistedElements = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
document.addEventListener("keydown", (event) => {
if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements
if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
case "Escape":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.hideSearchWords();
event.preventDefault();
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);

View file

@ -1,14 +0,0 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '1.0.6',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: false,
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 B

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,199 +0,0 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, is available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

View file

@ -1,82 +0,0 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #8f5902; font-style: italic } /* Comment */
.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */
.highlight .g { color: #000000 } /* Generic */
.highlight .k { color: #004461; font-weight: bold } /* Keyword */
.highlight .l { color: #000000 } /* Literal */
.highlight .n { color: #000000 } /* Name */
.highlight .o { color: #582800 } /* Operator */
.highlight .x { color: #000000 } /* Other */
.highlight .p { color: #000000; font-weight: bold } /* Punctuation */
.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #8f5902 } /* Comment.Preproc */
.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
.highlight .gd { color: #a40000 } /* Generic.Deleted */
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #ef2929 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #745334 } /* Generic.Prompt */
.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */
.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */
.highlight .ld { color: #000000 } /* Literal.Date */
.highlight .m { color: #990000 } /* Literal.Number */
.highlight .s { color: #4e9a06 } /* Literal.String */
.highlight .na { color: #c4a000 } /* Name.Attribute */
.highlight .nb { color: #004461 } /* Name.Builtin */
.highlight .nc { color: #000000 } /* Name.Class */
.highlight .no { color: #000000 } /* Name.Constant */
.highlight .nd { color: #888888 } /* Name.Decorator */
.highlight .ni { color: #ce5c00 } /* Name.Entity */
.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #000000 } /* Name.Function */
.highlight .nl { color: #f57900 } /* Name.Label */
.highlight .nn { color: #000000 } /* Name.Namespace */
.highlight .nx { color: #000000 } /* Name.Other */
.highlight .py { color: #000000 } /* Name.Property */
.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #000000 } /* Name.Variable */
.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */
.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */
.highlight .mb { color: #990000 } /* Literal.Number.Bin */
.highlight .mf { color: #990000 } /* Literal.Number.Float */
.highlight .mh { color: #990000 } /* Literal.Number.Hex */
.highlight .mi { color: #990000 } /* Literal.Number.Integer */
.highlight .mo { color: #990000 } /* Literal.Number.Oct */
.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */
.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */
.highlight .sc { color: #4e9a06 } /* Literal.String.Char */
.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */
.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */
.highlight .se { color: #4e9a06 } /* Literal.String.Escape */
.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */
.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */
.highlight .sx { color: #4e9a06 } /* Literal.String.Other */
.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */
.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */
.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */
.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #000000 } /* Name.Function.Magic */
.highlight .vc { color: #000000 } /* Name.Variable.Class */
.highlight .vg { color: #000000 } /* Name.Variable.Global */
.highlight .vi { color: #000000 } /* Name.Variable.Instance */
.highlight .vm { color: #000000 } /* Name.Variable.Magic */
.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */

View file

@ -1,531 +0,0 @@
/*
* searchtools.js
* ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilities for the full-text search.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, highlightTerms, searchTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const [docName, title, anchor, descr] = item;
let listItem = document.createElement("li");
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = docUrlRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = docUrlRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
const params = new URLSearchParams();
params.set("highlight", [...highlightTerms].join(" "));
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + "?" + params.toString() + anchor;
linkEl.innerHTML = title;
if (descr)
listItem.appendChild(document.createElement("span")).innerText =
" (" + descr + ")";
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms, highlightTerms)
);
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = _(
`Search finished, found ${resultCount} page(s) matching the search query.`
);
};
const _displayNextItem = (
results,
resultCount,
highlightTerms,
searchTerms
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), highlightTerms, searchTerms);
setTimeout(
() => _displayNextItem(results, resultCount, highlightTerms, searchTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString) => {
const htmlElement = document
.createRange()
.createContextualFragment(htmlString);
_removeChildren(htmlElement.querySelectorAll(".headerlink"));
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent !== undefined) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
query: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
// array of [docname, title, anchor, descr, score, filename]
let results = [];
_removeChildren(document.getElementById("search-progress"));
// lookup as object
objectTerms.forEach((term) =>
results.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
results.sort((a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
});
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
results = results.reverse();
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, highlightTerms, searchTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const docNames = Search._index.docnames;
const filenames = Search._index.filenames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
const arr = [
{ files: terms[word], score: Scorer.term },
{ files: titleTerms[word], score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord) && !terms[word])
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord) && !titleTerms[word])
arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
});
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, {});
scoreMap.get(file)[word] = record.score;
});
});
// create the mapping
files.forEach((file) => {
if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
fileMap.get(file).push(word);
else fileMap.set(file, [word]);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, highlightWords is the list of normal, unstemmed
* words. the first one is used to find the occurrence, the
* latter for highlighting it.
*/
makeSearchSummary: (htmlText, keywords, highlightWords) => {
const text = Search.htmlToText(htmlText).toLowerCase();
if (text === "") return null;
const actualStartPosition = [...keywords]
.map((k) => text.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("div");
summary.classList.add("context");
summary.innerText = top + text.substr(startWithContext, 240).trim() + tail;
highlightWords.forEach((highlightWord) =>
_highlightText(summary, highlightWord, "highlighted")
);
return summary;
},
};
_ready(Search.init);

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

1522
docs/build/genindex.html vendored

File diff suppressed because it is too large Load diff

View file

@ -1,4 +0,0 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 248bf4413f52c61df59cba2c064f51e2
tags: 645f666f9bcd5a90fca523b33c5a78b7

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,20 +0,0 @@
.. python-aternos documentation master file, created by
sphinx-quickstart on Fri Jun 17 14:02:03 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to python-aternos's documentation!
==========================================
.. toctree::
:maxdepth: 2
:caption: Contents:
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -1,7 +0,0 @@
python_aternos
==============
.. toctree::
:maxdepth: 4
python_aternos

View file

@ -1,93 +0,0 @@
python\_aternos package
=======================
Submodules
----------
python\_aternos.atclient module
-------------------------------
.. automodule:: python_aternos.atclient
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atconf module
-----------------------------
.. automodule:: python_aternos.atconf
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atconnect module
--------------------------------
.. automodule:: python_aternos.atconnect
:members:
:undoc-members:
:show-inheritance:
python\_aternos.aterrors module
-------------------------------
.. automodule:: python_aternos.aterrors
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atfile module
-----------------------------
.. automodule:: python_aternos.atfile
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atfm module
---------------------------
.. automodule:: python_aternos.atfm
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atjsparse module
--------------------------------
.. automodule:: python_aternos.atjsparse
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atplayers module
--------------------------------
.. automodule:: python_aternos.atplayers
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atserver module
-------------------------------
.. automodule:: python_aternos.atserver
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atwss module
----------------------------
.. automodule:: python_aternos.atwss
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: python_aternos
:members:
:undoc-members:
:show-inheritance:

View file

@ -1,134 +0,0 @@
/*
* _sphinx_javascript_frameworks_compat.js
* ~~~~~~~~~~
*
* Compatability shim for jQuery and underscores.js.
*
* WILL BE REMOVED IN Sphinx 6.0
* xref RemovedInSphinx60Warning
*
*/
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}

View file

@ -1,701 +0,0 @@
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Georgia, serif;
font-size: 17px;
background-color: #fff;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: 940px;
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 220px;
}
div.sphinxsidebar {
width: 220px;
font-size: 14px;
line-height: 1.5;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #fff;
color: #3E4349;
padding: 0 30px 0 30px;
}
div.body > .section {
text-align: left;
}
div.footer {
width: 940px;
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
p.caption {
font-family: inherit;
font-size: inherit;
}
div.relations {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0;
margin: -10px 0 0 0px;
text-align: center;
}
div.sphinxsidebarwrapper h1.logo {
margin-top: -10px;
text-align: center;
margin-bottom: 5px;
text-align: left;
}
div.sphinxsidebarwrapper h1.logo-name {
margin-top: 0px;
}
div.sphinxsidebarwrapper p.blurb {
margin-top: 0;
font-style: normal;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Georgia, serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar ul li.toctree-l1 > a {
font-size: 120%;
}
div.sphinxsidebar ul li.toctree-l2 > a {
font-size: 110%;
}
div.sphinxsidebar input {
border: 1px solid #CCC;
font-family: Georgia, serif;
font-size: 1em;
}
div.sphinxsidebar hr {
border: none;
height: 1px;
color: #AAA;
background: #AAA;
text-align: left;
margin-left: 0;
width: 50%;
}
div.sphinxsidebar .badge {
border-bottom: none;
}
div.sphinxsidebar .badge:hover {
border-bottom: none;
}
/* To address an issue with donation coming after search */
div.sphinxsidebar h3.donation {
margin-top: 10px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: Georgia, serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #DDD;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #EAEAEA;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
margin: 20px 0px;
padding: 10px 30px;
background-color: #EEE;
border: 1px solid #CCC;
}
div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fafafa;
}
div.admonition p.admonition-title {
font-family: Georgia, serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: #fff;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.warning {
background-color: #FCC;
border: 1px solid #FAA;
}
div.danger {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.error {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.caution {
background-color: #FCC;
border: 1px solid #FAA;
}
div.attention {
background-color: #FCC;
border: 1px solid #FAA;
}
div.important {
background-color: #EEE;
border: 1px solid #CCC;
}
div.note {
background-color: #EEE;
border: 1px solid #CCC;
}
div.tip {
background-color: #EEE;
border: 1px solid #CCC;
}
div.hint {
background-color: #EEE;
border: 1px solid #CCC;
}
div.seealso {
background-color: #EEE;
border: 1px solid #CCC;
}
div.topic {
background-color: #EEE;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt, code {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
.hll {
background-color: #FFC;
margin: 0 -12px;
padding: 0 12px;
display: block;
}
img.screenshot {
}
tt.descname, tt.descclassname, code.descname, code.descclassname {
font-size: 0.95em;
}
tt.descname, code.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #EEE;
background: #FDFDFD;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.field-list p {
margin-bottom: 0.8em;
}
/* Cloned from
* https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68
*/
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
table.footnote td.label {
width: .1px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
/* Matches the 30px from the narrow-screen "li > ul" selector below */
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #EEE;
padding: 7px 30px;
margin: 15px 0px;
line-height: 1.3em;
}
div.viewcode-block:target {
background: #ffd;
}
dl pre, blockquote pre, li pre {
margin-left: 0;
padding-left: 30px;
}
tt, code {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, code.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fff;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
/* Don't put an underline on images */
a.image-reference, a.image-reference:hover {
border-bottom: none;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt, a:hover code {
background: #EEE;
}
@media screen and (max-width: 870px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100%;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
li > ul {
/* Matches the 30px from the "ul, ol" selector above */
margin-left: 30px;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
@media screen and (max-width: 875px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: #fff;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: #FFF;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: #fff;
}
div.sphinxsidebar a {
color: #AAA;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
/* misc. */
.revsys-inline {
display: none!important;
}
/* Make nested-list/multi-paragraph items look better in Releases changelog
* pages. Without this, docutils' magical list fuckery causes inconsistent
* formatting between different release sub-lists.
*/
div#changelog > div.section > ul > li > p:only-child {
margin-bottom: 0;
}
/* Hide fugly table cell borders in ..bibliography:: directive output */
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
border: none;
/* Below needed in some edge cases; if not applied, bottom shadows appear */
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
/* relbar */
.related {
line-height: 30px;
width: 100%;
font-size: 0.9rem;
}
.related.top {
border-bottom: 1px solid #EEE;
margin-bottom: 20px;
}
.related.bottom {
border-top: 1px solid #EEE;
}
.related ul {
padding: 0;
margin: 0;
list-style: none;
}
.related li {
display: inline;
}
nav#rellinks {
float: right;
}
nav#rellinks li+li:before {
content: "|";
}
nav#breadcrumbs li+li:before {
content: "\00BB";
}
/* Hide certain items when printing */
@media print {
div.related {
display: none;
}
}

View file

@ -1,930 +0,0 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
/* Docutils 0.17 and older (footnotes & citations) */
dl.footnote > dt,
dl.citation > dt {
float: left;
margin-right: 0.5em;
}
dl.footnote > dd,
dl.citation > dd {
margin-bottom: 0em;
}
dl.footnote > dd:after,
dl.citation > dd:after {
content: "";
clear: both;
}
/* Docutils 0.18+ (footnotes & citations) */
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
/* Footnotes & citations ends */
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dt:after {
content: ":";
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

View file

@ -1 +0,0 @@
/* This file intentionally left blank. */

View file

@ -1,264 +0,0 @@
/*
* doctools.js
* ~~~~~~~~~~~
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0 &&
!parent.classList.contains(className) &&
!parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
parent.insertBefore(
span,
parent.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target)
);
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.highlightSearchWords();
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* highlight the search words provided in the url in the text
*/
highlightSearchWords: () => {
const highlight =
new URLSearchParams(window.location.search).get("highlight") || "";
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:Documentation.hideSearchWords()">' +
Documentation.gettext("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
const url = new URL(window.location);
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
const blacklistedElements = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
document.addEventListener("keydown", (event) => {
if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements
if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
case "Escape":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.hideSearchWords();
event.preventDefault();
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);

View file

@ -1,14 +0,0 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '1.0.6',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: false,
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 B

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,199 +0,0 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, is available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

View file

@ -1,82 +0,0 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #8f5902; font-style: italic } /* Comment */
.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */
.highlight .g { color: #000000 } /* Generic */
.highlight .k { color: #004461; font-weight: bold } /* Keyword */
.highlight .l { color: #000000 } /* Literal */
.highlight .n { color: #000000 } /* Name */
.highlight .o { color: #582800 } /* Operator */
.highlight .x { color: #000000 } /* Other */
.highlight .p { color: #000000; font-weight: bold } /* Punctuation */
.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #8f5902 } /* Comment.Preproc */
.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
.highlight .gd { color: #a40000 } /* Generic.Deleted */
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #ef2929 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #745334 } /* Generic.Prompt */
.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */
.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */
.highlight .ld { color: #000000 } /* Literal.Date */
.highlight .m { color: #990000 } /* Literal.Number */
.highlight .s { color: #4e9a06 } /* Literal.String */
.highlight .na { color: #c4a000 } /* Name.Attribute */
.highlight .nb { color: #004461 } /* Name.Builtin */
.highlight .nc { color: #000000 } /* Name.Class */
.highlight .no { color: #000000 } /* Name.Constant */
.highlight .nd { color: #888888 } /* Name.Decorator */
.highlight .ni { color: #ce5c00 } /* Name.Entity */
.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #000000 } /* Name.Function */
.highlight .nl { color: #f57900 } /* Name.Label */
.highlight .nn { color: #000000 } /* Name.Namespace */
.highlight .nx { color: #000000 } /* Name.Other */
.highlight .py { color: #000000 } /* Name.Property */
.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #000000 } /* Name.Variable */
.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */
.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */
.highlight .mb { color: #990000 } /* Literal.Number.Bin */
.highlight .mf { color: #990000 } /* Literal.Number.Float */
.highlight .mh { color: #990000 } /* Literal.Number.Hex */
.highlight .mi { color: #990000 } /* Literal.Number.Integer */
.highlight .mo { color: #990000 } /* Literal.Number.Oct */
.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */
.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */
.highlight .sc { color: #4e9a06 } /* Literal.String.Char */
.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */
.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */
.highlight .se { color: #4e9a06 } /* Literal.String.Escape */
.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */
.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */
.highlight .sx { color: #4e9a06 } /* Literal.String.Other */
.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */
.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */
.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */
.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #000000 } /* Name.Function.Magic */
.highlight .vc { color: #000000 } /* Name.Variable.Class */
.highlight .vg { color: #000000 } /* Name.Variable.Global */
.highlight .vi { color: #000000 } /* Name.Variable.Instance */
.highlight .vm { color: #000000 } /* Name.Variable.Magic */
.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */

View file

@ -1,531 +0,0 @@
/*
* searchtools.js
* ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilities for the full-text search.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, highlightTerms, searchTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const [docName, title, anchor, descr] = item;
let listItem = document.createElement("li");
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = docUrlRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = docUrlRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
const params = new URLSearchParams();
params.set("highlight", [...highlightTerms].join(" "));
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + "?" + params.toString() + anchor;
linkEl.innerHTML = title;
if (descr)
listItem.appendChild(document.createElement("span")).innerText =
" (" + descr + ")";
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms, highlightTerms)
);
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = _(
`Search finished, found ${resultCount} page(s) matching the search query.`
);
};
const _displayNextItem = (
results,
resultCount,
highlightTerms,
searchTerms
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), highlightTerms, searchTerms);
setTimeout(
() => _displayNextItem(results, resultCount, highlightTerms, searchTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString) => {
const htmlElement = document
.createRange()
.createContextualFragment(htmlString);
_removeChildren(htmlElement.querySelectorAll(".headerlink"));
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent !== undefined) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
query: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
// array of [docname, title, anchor, descr, score, filename]
let results = [];
_removeChildren(document.getElementById("search-progress"));
// lookup as object
objectTerms.forEach((term) =>
results.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
results.sort((a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
});
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
results = results.reverse();
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, highlightTerms, searchTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const docNames = Search._index.docnames;
const filenames = Search._index.filenames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
const arr = [
{ files: terms[word], score: Scorer.term },
{ files: titleTerms[word], score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord) && !terms[word])
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord) && !titleTerms[word])
arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
});
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, {});
scoreMap.get(file)[word] = record.score;
});
});
// create the mapping
files.forEach((file) => {
if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
fileMap.get(file).push(word);
else fileMap.set(file, [word]);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, highlightWords is the list of normal, unstemmed
* words. the first one is used to find the occurrence, the
* latter for highlighting it.
*/
makeSearchSummary: (htmlText, keywords, highlightWords) => {
const text = Search.htmlToText(htmlText).toLowerCase();
if (text === "") return null;
const actualStartPosition = [...keywords]
.map((k) => text.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("div");
summary.classList.add("context");
summary.innerText = top + text.substr(startWithContext, 240).trim() + tail;
highlightWords.forEach((highlightWord) =>
_highlightText(summary, highlightWord, "highlighted")
);
return summary;
},
};
_ready(Search.init);

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,101 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="#" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</html>

View file

@ -1,112 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<title>Welcome to python-aternos’s documentation! &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="welcome-to-python-aternos-s-documentation">
<h1>Welcome to python-aternos’s documentation!<a class="headerlink" href="#welcome-to-python-aternos-s-documentation" title="Permalink to this heading"></a></h1>
<div class="toctree-wrapper compound">
</div>
</section>
<section id="indices-and-tables">
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this heading"></a></h1>
<ul class="simple">
<li><p><a class="reference internal" href="genindex.html"><span class="std std-ref">Index</span></a></p></li>
<li><p><a class="reference internal" href="py-modindex.html"><span class="std std-ref">Module Index</span></a></p></li>
<li><p><a class="reference internal" href="search.html"><span class="std std-ref">Search Page</span></a></p></li>
</ul>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="#">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="#">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/index.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

View file

@ -1,121 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<title>python_aternos &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="python-aternos">
<h1>python_aternos<a class="headerlink" href="#python-aternos" title="Permalink to this heading"></a></h1>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="python_aternos.html">python_aternos package</a><ul>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#submodules">Submodules</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atclient-module">python_aternos.atclient module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atconf-module">python_aternos.atconf module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atconnect-module">python_aternos.atconnect module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-aterrors-module">python_aternos.aterrors module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atfile-module">python_aternos.atfile module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atfm-module">python_aternos.atfm module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atjsparse-module">python_aternos.atjsparse module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atplayers-module">python_aternos.atplayers module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atserver-module">python_aternos.atserver module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#python-aternos-atwss-module">python_aternos.atwss module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-contents">Module contents</a></li>
</ul>
</li>
</ul>
</div>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/modules.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

View file

@ -1,7 +0,0 @@
# Sphinx inventory version 2
# Project: python-aternos
# Version:
# The remainder of this file is compressed using zlib.
xÚ…�A
Â0E÷9Åx€
n½� ¡àÂ¥ÄdhŠI&4S°;¯áõ<‰¶‰˜@Á]òÿ›ÿ:ô½×x‡Èzoå-4;èrº5ì,æ§øašÔ Ä­"‡ÀabC¾‘Œƒ§øz<#|øÑ¡gÉ=ù�p¤W”aj¾EÚ<’-f¹[>±Ôç(ÁÉzÉVQŒý±´Ëa-«ÇJgݬ©!Hu“ŠˆrP¦¶§,��RßÎìÈÑŽs

View file

@ -1,138 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<title>python_aternos package &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="python-aternos-package">
<h1>python_aternos package<a class="headerlink" href="#python-aternos-package" title="Permalink to this heading"></a></h1>
<section id="submodules">
<h2>Submodules<a class="headerlink" href="#submodules" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atclient-module">
<h2>python_aternos.atclient module<a class="headerlink" href="#python-aternos-atclient-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atconf-module">
<h2>python_aternos.atconf module<a class="headerlink" href="#python-aternos-atconf-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atconnect-module">
<h2>python_aternos.atconnect module<a class="headerlink" href="#python-aternos-atconnect-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-aterrors-module">
<h2>python_aternos.aterrors module<a class="headerlink" href="#python-aternos-aterrors-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atfile-module">
<h2>python_aternos.atfile module<a class="headerlink" href="#python-aternos-atfile-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atfm-module">
<h2>python_aternos.atfm module<a class="headerlink" href="#python-aternos-atfm-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atjsparse-module">
<h2>python_aternos.atjsparse module<a class="headerlink" href="#python-aternos-atjsparse-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atplayers-module">
<h2>python_aternos.atplayers module<a class="headerlink" href="#python-aternos-atplayers-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atserver-module">
<h2>python_aternos.atserver module<a class="headerlink" href="#python-aternos-atserver-module" title="Permalink to this heading"></a></h2>
</section>
<section id="python-aternos-atwss-module">
<h2>python_aternos.atwss module<a class="headerlink" href="#python-aternos-atwss-module" title="Permalink to this heading"></a></h2>
</section>
<section id="module-contents">
<h2>Module contents<a class="headerlink" href="#module-contents" title="Permalink to this heading"></a></h2>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/python_aternos.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

View file

@ -1,120 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/searchtools.js"></script>
<script src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="#" />
<script src="searchindex.js" defer></script>
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1 id="search-documentation">Search</h1>
<noscript>
<div class="admonition warning">
<p>
Please activate JavaScript to enable the search
functionality.
</p>
</div>
</noscript>
<p>
Searching for multiple words only shows matches that contain
all words.
</p>
<form action="" method="get">
<input type="text" name="q" aria-labelledby="search-documentation" value="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
<div id="search-results">
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</html>

View file

@ -1 +0,0 @@
Search.setIndex({"docnames": ["index", "modules", "python_aternos"], "filenames": ["index.rst", "modules.rst", "python_aternos.rst"], "titles": ["Welcome to python-aternos\u2019s documentation!", "python_aternos", "python_aternos package"], "terms": {"index": 0, "modul": [0, 1], "search": 0, "page": 0, "packag": 1, "submodul": 1, "atclient": 1, "atconf": 1, "atconnect": 1, "aterror": 1, "atfil": 1, "atfm": 1, "atjspars": 1, "atplay": 1, "atserv": 1, "atwss": 1, "content": 1}, "objects": {}, "objtypes": {}, "objnames": {}, "titleterms": {"welcom": 0, "python": 0, "aterno": 0, "s": 0, "document": 0, "indic": 0, "tabl": 0, "python_aterno": [1, 2], "packag": 2, "submodul": 2, "atclient": 2, "modul": 2, "atconf": 2, "atconnect": 2, "aterror": 2, "atfil": 2, "atfm": 2, "atjspars": 2, "atplay": 2, "atserv": 2, "atwss": 2, "content": 2}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 6, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 56}})

112
docs/build/index.html vendored
View file

@ -1,112 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<title>Welcome to python-aternos’s documentation! &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="welcome-to-python-aternos-s-documentation">
<h1>Welcome to python-aternos’s documentation!<a class="headerlink" href="#welcome-to-python-aternos-s-documentation" title="Permalink to this heading"></a></h1>
<div class="toctree-wrapper compound">
</div>
</section>
<section id="indices-and-tables">
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this heading"></a></h1>
<ul class="simple">
<li><p><a class="reference internal" href="genindex.html"><span class="std std-ref">Index</span></a></p></li>
<li><p><a class="reference internal" href="py-modindex.html"><span class="std std-ref">Module Index</span></a></p></li>
<li><p><a class="reference internal" href="search.html"><span class="std std-ref">Search Page</span></a></p></li>
</ul>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="#">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="#">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/index.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

View file

@ -1,122 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<title>python-aternos &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="python-aternos">
<h1>python-aternos<a class="headerlink" href="#python-aternos" title="Permalink to this heading"></a></h1>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="python_aternos.html">python_aternos package</a><ul>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#submodules">Submodules</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atclient">python_aternos.atclient module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atconf">python_aternos.atconf module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atconnect">python_aternos.atconnect module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.aterrors">python_aternos.aterrors module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atfile">python_aternos.atfile module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atfm">python_aternos.atfm module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atjsparse">python_aternos.atjsparse module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atplayers">python_aternos.atplayers module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atserver">python_aternos.atserver module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos.atwss">python_aternos.atwss module</a></li>
<li class="toctree-l2"><a class="reference internal" href="python_aternos.html#module-python_aternos">Module contents</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="setup.html">setup module</a></li>
</ul>
</div>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/modules.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

BIN
docs/build/objects.inv vendored

Binary file not shown.

View file

@ -1,166 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Python Module Index &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Python Module Index</h1>
<div class="modindex-jumpbox">
<a href="#cap-p"><strong>p</strong></a>
</div>
<table class="indextable modindextable">
<tr class="pcap"><td></td><td>&#160;</td><td></td></tr>
<tr class="cap" id="cap-p"><td></td><td>
<strong>p</strong></td><td></td></tr>
<tr>
<td><img src="_static/minus.png" class="toggler"
id="toggle-1" style="display: none" alt="-" /></td>
<td>
<a href="python_aternos.html#module-python_aternos"><code class="xref">python_aternos</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atclient"><code class="xref">python_aternos.atclient</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atconf"><code class="xref">python_aternos.atconf</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atconnect"><code class="xref">python_aternos.atconnect</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.aterrors"><code class="xref">python_aternos.aterrors</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atfile"><code class="xref">python_aternos.atfile</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atfm"><code class="xref">python_aternos.atfm</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atjsparse"><code class="xref">python_aternos.atjsparse</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atplayers"><code class="xref">python_aternos.atplayers</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atserver"><code class="xref">python_aternos.atserver</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="python_aternos.html#module-python_aternos.atwss"><code class="xref">python_aternos.atwss</code></a></td><td>
<em></em></td></tr>
</table>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</html>

File diff suppressed because it is too large Load diff

120
docs/build/search.html vendored
View file

@ -1,120 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/searchtools.js"></script>
<script src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="#" />
<script src="searchindex.js" defer></script>
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1 id="search-documentation">Search</h1>
<noscript>
<div class="admonition warning">
<p>
Please activate JavaScript to enable the search
functionality.
</p>
</div>
</noscript>
<p>
Searching for multiple words only shows matches that contain
all words.
</p>
<form action="" method="get">
<input type="text" name="q" aria-labelledby="search-documentation" value="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
<div id="search-results">
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

102
docs/build/setup.html vendored
View file

@ -1,102 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<title>setup module &#8212; python-aternos 1.0.6 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="setup-module">
<h1>setup module<a class="headerlink" href="#setup-module" title="Permalink to this heading"></a></h1>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">python-aternos</a></h1>
<h3>Navigation</h3>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2022, DarkCat09.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.0.2</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/setup.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

1
docs/howto.md Normal file
View file

@ -0,0 +1 @@
Very interesting information

4
docs/index.md Normal file
View file

@ -0,0 +1,4 @@
![Python-Aternos Logo](https://i.ibb.co/3RXcXJ1/aternos-400.png)
# Python Aternos
An unofficial Aternos API written in Python.
It uses [aternos](https://aternos.org/)' private API and html parsing.

View file

@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

39
docs/reference.md Normal file
View file

@ -0,0 +1,39 @@
::: python_aternos.atclient
options:
show_source: false
::: python_aternos.atserver
options:
show_source: false
::: python_aternos.atplayers
options:
show_source: false
::: python_aternos.atconf
options:
show_source: false
::: python_aternos.atfm
options:
show_source: false
::: python_aternos.atfile
options:
show_source: false
::: python_aternos.atwss
options:
show_source: false
::: python_aternos.atconnect
options:
show_source: false
::: python_aternos.atjsparse
options:
show_source: false
::: python_aternos.aterrors
options:
show_source: false

View file

@ -1,53 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
import os
import sys
sys.path.insert(0, os.path.abspath('../../../'))
# -- Project information -----------------------------------------------------
project = 'python-aternos'
copyright = '2022, DarkCat09'
author = 'DarkCat09'
# The full version, including alpha/beta/rc tags
release = '1.0.6'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

View file

@ -1,20 +0,0 @@
.. python-aternos documentation master file, created by
sphinx-quickstart on Fri Jun 17 14:02:03 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to python-aternos's documentation!
==========================================
.. toctree::
:maxdepth: 2
:caption: Contents:
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -1,8 +0,0 @@
python-aternos
==============
.. toctree::
:maxdepth: 4
python_aternos
setup

View file

@ -1,93 +0,0 @@
python\_aternos package
=======================
Submodules
----------
python\_aternos.atclient module
-------------------------------
.. automodule:: python_aternos.atclient
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atconf module
-----------------------------
.. automodule:: python_aternos.atconf
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atconnect module
--------------------------------
.. automodule:: python_aternos.atconnect
:members:
:undoc-members:
:show-inheritance:
python\_aternos.aterrors module
-------------------------------
.. automodule:: python_aternos.aterrors
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atfile module
-----------------------------
.. automodule:: python_aternos.atfile
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atfm module
---------------------------
.. automodule:: python_aternos.atfm
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atjsparse module
--------------------------------
.. automodule:: python_aternos.atjsparse
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atplayers module
--------------------------------
.. automodule:: python_aternos.atplayers
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atserver module
-------------------------------
.. automodule:: python_aternos.atserver
:members:
:undoc-members:
:show-inheritance:
python\_aternos.atwss module
----------------------------
.. automodule:: python_aternos.atwss
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: python_aternos
:members:
:undoc-members:
:show-inheritance:

View file

@ -1,7 +0,0 @@
setup module
============
.. automodule:: setup
:members:
:undoc-members:
:show-inheritance:

View file

@ -16,6 +16,7 @@ if resp:
async def console(msg): async def console(msg):
print('<', msg) print('<', msg)
async def main(): async def main():
s.start() s.start()
await asyncio.gather( await asyncio.gather(
@ -23,6 +24,7 @@ async def main():
commands() commands()
) )
async def commands(): async def commands():
while True: while True:
cmd = await aioconsole.ainput('> ') cmd = await aioconsole.ainput('> ')

View file

@ -1,5 +1,5 @@
from getpass import getpass from getpass import getpass
from python_aternos import Client, atserver from python_aternos import Client
user = input('Username: ') user = input('Username: ')
pswd = getpass('Password: ') pswd = getpass('Password: ')

View file

@ -15,15 +15,18 @@ aternos = Client.from_credentials(user, pswd)
s = aternos.list_servers()[0] s = aternos.list_servers()[0]
socket = s.wss() socket = s.wss()
@socket.wssreceiver(atwss.Streams.console, 'Server 1') @socket.wssreceiver(atwss.Streams.console, 'Server 1')
async def console(msg, args): async def console(msg, args):
print(args[0], 'received', msg) print(args[0], 'received', msg)
async def main(): async def main():
s.start() s.start()
await socket.connect() await socket.connect()
await asyncio.create_task(loop()) await asyncio.create_task(loop())
async def loop(): async def loop():
while True: while True:
await asyncio.sleep(1) await asyncio.sleep(1)

View file

@ -9,15 +9,18 @@ aternos = Client.from_credentials(user, pswd)
s = aternos.list_servers()[0] s = aternos.list_servers()[0]
socket = s.wss() socket = s.wss()
@socket.wssreceiver(atwss.Streams.console) @socket.wssreceiver(atwss.Streams.console)
async def console(msg): async def console(msg):
print('Received:', msg) print('Received:', msg)
async def main(): async def main():
s.start() s.start()
await socket.connect() await socket.connect()
await asyncio.create_task(loop()) await asyncio.create_task(loop())
async def loop(): async def loop():
while True: while True:
await asyncio.sleep(1) await asyncio.sleep(1)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

13
mkdocs.yml Normal file
View file

@ -0,0 +1,13 @@
site_name: Python-Aternos
theme:
name: readthedocs
plugins:
- search
- mkdocstrings
nav:
- index.md
- howto.md
- reference.md

View file

@ -31,20 +31,20 @@ from .atjsparse import to_ecma5_function
__all__ = [ __all__ = [
'atclient', 'atserver', 'atconnect', 'atclient', 'atserver', 'atconnect',
'atplayers', 'atconf', 'atwss', 'atplayers', 'atconf', 'atwss',
'atfm', 'atfile', 'atfm', 'atfile',
'aterrors', 'atjsparse', 'aterrors', 'atjsparse',
'Client', 'AternosServer', 'AternosConnect', 'Client', 'AternosServer', 'AternosConnect',
'PlayersList', 'AternosConfig', 'AternosWss', 'PlayersList', 'AternosConfig', 'AternosWss',
'FileManager', 'AternosFile', 'AternosError', 'FileManager', 'AternosFile', 'AternosError',
'CloudflareError', 'CredentialsError', 'TokenError', 'CloudflareError', 'CredentialsError', 'TokenError',
'ServerError', 'ServerEulaError', 'ServerRunningError', 'ServerError', 'ServerEulaError', 'ServerRunningError',
'ServerSoftwareError', 'ServerStorageError', 'FileError', 'ServerSoftwareError', 'ServerStorageError', 'FileError',
'exec', 'atob', 'to_ecma5_function', 'exec', 'atob', 'to_ecma5_function',
'Edition', 'Status', 'Lists', 'Edition', 'Status', 'Lists',
'ServerOpts', 'WorldOpts', 'WorldRules', 'ServerOpts', 'WorldOpts', 'WorldRules',
'Gamemode', 'Difficulty', 'Streams', 'FileType', 'Gamemode', 'Difficulty', 'Streams', 'FileType',
] ]

View file

@ -8,208 +8,218 @@ from .atserver import AternosServer
from .atconnect import AternosConnect from .atconnect import AternosConnect
from .aterrors import CredentialsError from .aterrors import CredentialsError
class Client: class Client:
"""Aternos API Client class whose object contains user's auth data """Aternos API Client class whose object contains user's auth data
:param atconn: :class:`python_aternos.atconnect.AternosConnect` instance with initialized Aternos session :param atconn: :class:`python_aternos.atconnect.AternosConnect`
:type atconn: python_aternos.atconnect.AternosConnect instance with initialized Aternos session
""" :type atconn: python_aternos.atconnect.AternosConnect
"""
def __init__(self, atconn:AternosConnect) -> None: def __init__(self, atconn: AternosConnect) -> None:
self.atconn = atconn self.atconn = atconn
@classmethod @classmethod
def from_hashed(cls, username:str, md5:str): def from_hashed(cls, username: str, md5: str):
"""Log in to Aternos with a username and a hashed password """Log in to Aternos with a username and a hashed password
:param username: Your username :param username: Your username
:type username: str :type username: str
:param md5: Your password hashed with MD5 :param md5: Your password hashed with MD5
:type md5: str :type md5: str
:raises CredentialsError: If the API doesn't return a valid session cookie :raises CredentialsError: If the API
:return: Client instance doesn't return a valid session cookie
:rtype: python_aternos.Client :return: Client instance
""" :rtype: python_aternos.Client
"""
atconn = AternosConnect() atconn = AternosConnect()
atconn.parse_token() atconn.parse_token()
atconn.generate_sec() atconn.generate_sec()
credentials = { credentials = {
'user': username, 'user': username,
'password': md5 'password': md5
} }
loginreq = atconn.request_cloudflare( loginreq = atconn.request_cloudflare(
f'https://aternos.org/panel/ajax/account/login.php', f'https://aternos.org/panel/ajax/account/login.php',
'POST', data=credentials, sendtoken=True 'POST', data=credentials, sendtoken=True
) )
if 'ATERNOS_SESSION' not in loginreq.cookies: if 'ATERNOS_SESSION' not in loginreq.cookies:
raise CredentialsError( raise CredentialsError(
'Check your username and password' 'Check your username and password'
) )
return cls(atconn) return cls(atconn)
@classmethod @classmethod
def from_credentials(cls, username:str, password:str): def from_credentials(cls, username: str, password: str):
"""Log in to Aternos with a username and a plain password """Log in to Aternos with a username and a plain password
:param username: Your username :param username: Your username
:type username: str :type username: str
:param password: Your password without any encryption :param password: Your password without any encryption
:type password: str :type password: str
:return: Client instance :return: Client instance
:rtype: python_aternos.Client :rtype: python_aternos.Client
""" """
md5 = Client.md5encode(password) md5 = Client.md5encode(password)
return cls.from_hashed(username, md5) return cls.from_hashed(username, md5)
@classmethod @classmethod
def from_session(cls, session:str): def from_session(cls, session: str):
"""Log in to Aternos using a session cookie value """Log in to Aternos using a session cookie value
:param session: Value of ATERNOS_SESSION cookie :param session: Value of ATERNOS_SESSION cookie
:type session: str :type session: str
:return: Client instance :return: Client instance
:rtype: python_aternos.Client :rtype: python_aternos.Client
""" """
atconn = AternosConnect() atconn = AternosConnect()
atconn.session.cookies['ATERNOS_SESSION'] = session atconn.session.cookies['ATERNOS_SESSION'] = session
atconn.parse_token() atconn.parse_token()
atconn.generate_sec() atconn.generate_sec()
return cls(atconn) return cls(atconn)
@classmethod @classmethod
def restore_session(cls, file:str='~/.aternos'): def restore_session(cls, file: str = '~/.aternos'):
"""Log in to Aternos using a saved ATERNOS_SESSION cookie """Log in to Aternos using a saved ATERNOS_SESSION cookie
:param file: File where a session cookie was saved, deafults to ~/.aternos :param file: File where a session cookie
:type file: str, optional was saved, deafults to ~/.aternos
:return: Client instance :type file: str, optional
:rtype: python_aternos.Client :return: Client instance
""" :rtype: python_aternos.Client
"""
file = os.path.expanduser(file) file = os.path.expanduser(file)
with open(file, 'rt') as f: with open(file, 'rt') as f:
session = f.read().strip() session = f.read().strip()
return cls.from_session(session) return cls.from_session(session)
@staticmethod @staticmethod
def md5encode(passwd:str) -> str: def md5encode(passwd: str) -> str:
"""Encodes the given string with MD5 """Encodes the given string with MD5
:param passwd: String to encode :param passwd: String to encode
:type passwd: str :type passwd: str
:return: Hexdigest hash of the string in lowercase :return: Hexdigest hash of the string in lowercase
:rtype: str :rtype: str
""" """
encoded = hashlib.md5(passwd.encode('utf-8')) encoded = hashlib.md5(passwd.encode('utf-8'))
return encoded.hexdigest().lower() return encoded.hexdigest().lower()
def save_session(self, file:str='~/.aternos') -> None: def save_session(self, file: str = '~/.aternos') -> None:
"""Saves an ATERNOS_SESSION cookie to a file """Saves an ATERNOS_SESSION cookie to a file
:param file: File where a session cookie must be saved, defaults to ~/.aternos :param file: File where a session cookie
:type file: str, optional must be saved, defaults to ~/.aternos
""" :type file: str, optional
"""
file = os.path.expanduser(file) file = os.path.expanduser(file)
with open(file, 'wt') as f: with open(file, 'wt') as f:
f.write(self.atconn.atsession) f.write(self.atconn.atsession)
def list_servers(self) -> List[AternosServer]: def list_servers(self) -> List[AternosServer]:
"""Parses a list of your servers from Aternos website """Parses a list of your servers from Aternos website
:return: List of :class:`python_aternos.atserver.AternosServer` objects :return: List of :class:`python_aternos.atserver.AternosServer` objects
:rtype: list :rtype: list
""" """
serverspage = self.atconn.request_cloudflare( serverspage = self.atconn.request_cloudflare(
'https://aternos.org/servers/', 'GET' 'https://aternos.org/servers/', 'GET'
) )
serverstree = lxml.html.fromstring(serverspage.content) serverstree = lxml.html.fromstring(serverspage.content)
serverslist = serverstree.xpath('//div[contains(@class,"servers ")]/div') serverslist = serverstree.xpath(
'//div[contains(@class,"servers ")]/div'
)
servers = [] servers = []
for server in serverslist: for server in serverslist:
servid = server.xpath('./div[@class="server-body"]/@data-id')[0] servid = server.xpath('./div[@class="server-body"]/@data-id')[0]
servers.append(AternosServer(servid, self.atconn)) servers.append(AternosServer(servid, self.atconn))
return servers return servers
def get_server(self, servid:str) -> AternosServer: def get_server(self, servid: str) -> AternosServer:
"""Creates a server object from the server ID. """Creates a server object from the server ID.
Use this instead of list_servers if you know the ID to save some time. Use this instead of list_servers if you know the ID to save some time.
:return: :class:`python_aternos.atserver.AternosServer` object :return: :class:`python_aternos.atserver.AternosServer` object
:rtype: python_aternos.atserver.AternosServer :rtype: python_aternos.atserver.AternosServer
""" """
return AternosServer(servid, self.atconn) return AternosServer(servid, self.atconn)
def change_username(self, value:str) -> None: def change_username(self, value: str) -> None:
"""Changes a username in your Aternos account """Changes a username in your Aternos account
:param value: New username :param value: New username
:type value: str :type value: str
""" """
self.atconn.request_cloudflare( self.atconn.request_cloudflare(
'https://aternos.org/panel/ajax/account/username.php', 'https://aternos.org/panel/ajax/account/username.php',
'POST', data={'username': value} 'POST', data={'username': value}
) )
def change_email(self, value:str) -> None: def change_email(self, value: str) -> None:
"""Changes an e-mail in your Aternos account """Changes an e-mail in your Aternos account
:param value: New e-mail :param value: New e-mail
:type value: str :type value: str
:raises ValueError: If an invalid e-mail address is passed to the function :raises ValueError: If an invalid
""" e-mail address was passed to the function
"""
email = re.compile(r'^[A-Za-z0-9\-_+.]+@[A-Za-z0-9\-_+.]+\.[A-Za-z0-9\-]+$|^$') email = re.compile(
if not email.match(value): r'^[A-Za-z0-9\-_+.]+@[A-Za-z0-9\-_+.]+\.[A-Za-z0-9\-]+$|^$'
raise ValueError('Invalid e-mail!') )
if not email.match(value):
raise ValueError('Invalid e-mail!')
self.atconn.request_cloudflare( self.atconn.request_cloudflare(
'https://aternos.org/panel/ajax/account/email.php', 'https://aternos.org/panel/ajax/account/email.php',
'POST', data={'email': value} 'POST', data={'email': value}
) )
def change_password(self, old:str, new:str) -> None: def change_password(self, old: str, new: str) -> None:
"""Changes a password in your Aternos account """Changes a password in your Aternos account
:param old: Old password :param old: Old password
:type old: str :type old: str
:param new: New password :param new: New password
:type new: str :type new: str
""" """
old = Client.md5encode(old) old = Client.md5encode(old)
new = Client.md5encode(new) new = Client.md5encode(new)
self.atconn.request_cloudflare( self.atconn.request_cloudflare(
'https://aternos.org/panel/ajax/account/password.php', 'https://aternos.org/panel/ajax/account/password.php',
'POST', data={ 'POST', data={
'oldpassword': old, 'oldpassword': old,
'newpassword': new 'newpassword': new
} }
) )

View file

@ -5,327 +5,346 @@ from typing import Any, Dict, List, Union, Optional
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from .atserver import AternosServer from .atserver import AternosServer
class ServerOpts(enum.Enum):
"""server.options file"""
players = 'max-players'
gm = 'gamemode'
difficulty = 'difficulty'
whl = 'white-list'
online = 'online-mode'
pvp = 'pvp'
cmdblock = 'enable-command-block'
flight = 'allow-flight'
animals = 'spawn-animals'
monsters = 'spawn-monsters'
villagers = 'spawn-npcs'
nether = 'allow-nether'
forcegm = 'force-gamemode'
spawnlock = 'spawn-protection'
cmds = 'allow-cheats'
packreq = 'require-resource-pack'
pack = 'resource-pack'
DAT_PREFIX = 'Data:' DAT_PREFIX = 'Data:'
DAT_GR_PREFIX = 'Data:GameRules:' DAT_GR_PREFIX = 'Data:GameRules:'
class ServerOpts(enum.Enum):
"""server.options file"""
players = 'max-players'
gm = 'gamemode'
difficulty = 'difficulty'
whl = 'white-list'
online = 'online-mode'
pvp = 'pvp'
cmdblock = 'enable-command-block'
flight = 'allow-flight'
animals = 'spawn-animals'
monsters = 'spawn-monsters'
villagers = 'spawn-npcs'
nether = 'allow-nether'
forcegm = 'force-gamemode'
spawnlock = 'spawn-protection'
cmds = 'allow-cheats'
packreq = 'require-resource-pack'
pack = 'resource-pack'
class WorldOpts(enum.Enum): class WorldOpts(enum.Enum):
"""level.dat file""" """level.dat file"""
seed12 = 'randomseed'
seed = 'seed'
hardcore = 'hardcore'
difficulty = 'Difficulty'
seed12 = 'randomseed'
seed = 'seed'
hardcore = 'hardcore'
difficulty = 'Difficulty'
class WorldRules(enum.Enum): class WorldRules(enum.Enum):
"""/gamerule list""" """/gamerule list"""
advs = 'announceAdvancements'
univanger = 'universalAnger'
cmdout = 'commandBlockOutput'
elytra = 'disableElytraMovementCheck'
raids = 'disableRaids'
daynight = 'doDaylightCycle'
entdrop = 'doEntityDrops'
fire = 'doFireTick'
phantoms = 'doInsomnia'
immrespawn = 'doImmediateRespawn'
limitcraft = 'doLimitedCrafting'
mobloot = 'doMobLoot'
mobs = 'doMobSpawning'
patrols = 'doPatrolSpawning'
blockdrop = 'doTileDrops'
traders = 'doTraderSpawning'
weather = 'doWeatherCycle'
drowndmg = 'drowningDamage'
falldmg = 'fallDamage'
firedmg = 'fireDamage'
snowdmg = 'freezeDamage'
forgive = 'forgiveDeadPlayers'
keepinv = 'keepInventory'
deathmsg = 'showDeathMessages'
admincmdlog = 'logAdminCommands'
cmdlen = 'maxCommandChainLength'
entcram = 'maxEntityCramming'
mobgrief = 'mobGriefing'
regen = 'naturalRegeneration'
sleeppct = 'playersSleepingPercentage'
rndtick = 'randomTickSpeed'
spawnradius = 'spawnRadius'
reducedf3 = 'reducedDebugInfo'
spectchunkgen = 'spectatorsGenerateChunks'
cmdfb = 'sendCommandFeedback'
advs = 'announceAdvancements'
univanger = 'universalAnger'
cmdout = 'commandBlockOutput'
elytra = 'disableElytraMovementCheck'
raids = 'disableRaids'
daynight = 'doDaylightCycle'
entdrop = 'doEntityDrops'
fire = 'doFireTick'
phantoms = 'doInsomnia'
immrespawn = 'doImmediateRespawn'
limitcraft = 'doLimitedCrafting'
mobloot = 'doMobLoot'
mobs = 'doMobSpawning'
patrols = 'doPatrolSpawning'
blockdrop = 'doTileDrops'
traders = 'doTraderSpawning'
weather = 'doWeatherCycle'
drowndmg = 'drowningDamage'
falldmg = 'fallDamage'
firedmg = 'fireDamage'
snowdmg = 'freezeDamage'
forgive = 'forgiveDeadPlayers'
keepinv = 'keepInventory'
deathmsg = 'showDeathMessages'
admincmdlog = 'logAdminCommands'
cmdlen = 'maxCommandChainLength'
entcram = 'maxEntityCramming'
mobgrief = 'mobGriefing'
regen = 'naturalRegeneration'
sleeppct = 'playersSleepingPercentage'
rndtick = 'randomTickSpeed'
spawnradius = 'spawnRadius'
reducedf3 = 'reducedDebugInfo'
spectchunkgen = 'spectatorsGenerateChunks'
cmdfb = 'sendCommandFeedback'
class Gamemode(enum.IntEnum): class Gamemode(enum.IntEnum):
"""/gamemode numeric list""" """/gamemode numeric list"""
survival = 0
creative = 1
adventure = 2
spectator = 3
survival = 0
creative = 1
adventure = 2
spectator = 3
class Difficulty(enum.IntEnum): class Difficulty(enum.IntEnum):
"""/difficulty numeric list""" """/difficulty numeric list"""
peaceful = 0
easy = 1
normal = 2
hard = 3
peaceful = 0
easy = 1
normal = 2
hard = 3
# checking timezone format # checking timezone format
tzcheck = re.compile(r'(^[A-Z]\w+\/[A-Z]\w+$)|^UTC$') tzcheck = re.compile(r'(^[A-Z]\w+\/[A-Z]\w+$)|^UTC$')
# options types converting # options types converting
convert = { convert = {
'config-option-number': int, 'config-option-number': int,
'config-option-select': int, 'config-option-select': int,
'config-option-toggle': bool 'config-option-toggle': bool
} }
class AternosConfig: class AternosConfig:
"""Class for editing server settings """Class for editing server settings
:param atserv: :class:`python_aternos.atserver.AternosServer` object :param atserv: :class:`python_aternos.atserver.AternosServer` object
:type atserv: python_aternos.atserver.AternosServer :type atserv: python_aternos.atserver.AternosServer
""" """
def __init__(self, atserv:'AternosServer') -> None: def __init__(self, atserv: 'AternosServer') -> None:
self.atserv = atserv self.atserv = atserv
def get_timezone(self) -> str: def get_timezone(self) -> str:
"""Parses timezone from options page """Parses timezone from options page
:return: Area/Location :return: Area/Location
:rtype: str :rtype: str
""" """
optreq = self.atserv.atserver_request( optreq = self.atserv.atserver_request(
'https://aternos.org/options', 'GET' 'https://aternos.org/options', 'GET'
) )
opttree = lxml.html.fromstring(optreq) opttree = lxml.html.fromstring(optreq)
tzopt = opttree.xpath('//div[@class="options-other-input timezone-switch"]')[0] tzopt = opttree.xpath(
tztext = tzopt.xpath('.//div[@class="option current"]')[0].text '//div[@class="options-other-input timezone-switch"]'
return tztext.strip() )[0]
tztext = tzopt.xpath('.//div[@class="option current"]')[0].text
return tztext.strip()
def set_timezone(self, value:str) -> None: def set_timezone(self, value: str) -> None:
"""Sets new timezone """Sets new timezone
:param value: New timezone :param value: New timezone
:type value: str :type value: str
:raises ValueError: If given string :raises ValueError: If given string
doesn't match Area/Location format doesn't match Area/Location format
""" """
matches_tz = tzcheck.search(value) matches_tz = tzcheck.search(value)
if not matches_tz: if not matches_tz:
raise ValueError('Timezone must match zoneinfo format: Area/Location') raise ValueError(
'Timezone must match zoneinfo format: Area/Location'
)
self.atserv.atserver_request( self.atserv.atserver_request(
'https://aternos.org/panel/ajax/timezone.php', 'https://aternos.org/panel/ajax/timezone.php',
'POST', data={'timezone': value}, 'POST', data={'timezone': value},
sendtoken=True sendtoken=True
) )
def get_java(self) -> int: def get_java(self) -> int:
"""Parses Java version from options page """Parses Java version from options page
:return: Java image version :return: Java image version
:rtype: int :rtype: int
""" """
optreq = self.atserv.atserver_request( optreq = self.atserv.atserver_request(
'https://aternos.org/options', 'GET' 'https://aternos.org/options', 'GET'
) )
opttree = lxml.html.fromstring(optreq) opttree = lxml.html.fromstring(optreq)
imgopt = opttree.xpath('//div[@class="options-other-input image-switch"]')[0] imgopt = opttree.xpath(
imgver = imgopt.xpath('.//div[@class="option current"]/@data-value')[0] '//div[@class="options-other-input image-switch"]'
)[0]
imgver = imgopt.xpath(
'.//div[@class="option current"]/@data-value'
)[0]
jdkver = str(imgver or '').removeprefix('openjdk:') jdkver = str(imgver or '').removeprefix('openjdk:')
return int(jdkver) return int(jdkver)
def set_java(self, value:int) -> None: def set_java(self, value: int) -> None:
"""Sets new Java version """Sets new Java version
:param value: New Java image version :param value: New Java image version
:type value: int :type value: int
""" """
self.atserv.atserver_request( self.atserv.atserver_request(
'https://aternos.org/panel/ajax/image.php', 'https://aternos.org/panel/ajax/image.php',
'POST', data={'image': f'openjdk:{value}'}, 'POST', data={'image': f'openjdk:{value}'},
sendtoken=True sendtoken=True
) )
# #
# server.properties # server.properties
# #
def set_server_prop(self, option:str, value:Any) -> None: def set_server_prop(self, option: str, value: Any) -> None:
"""Sets server.properties option """Sets server.properties option
:param option: Option name :param option: Option name
:type option: str :type option: str
:param value: New value :param value: New value
:type value: Any :type value: Any
""" """
self.__set_prop( self.__set_prop(
'/server.properties', '/server.properties',
option, value option, value
) )
def get_server_props(self, proptyping:bool=True) -> Dict[str,Any]: def get_server_props(self, proptyping: bool = True) -> Dict[str, Any]:
"""Parses all server.properties from options page """Parses all server.properties from options page
:param proptyping: If the returned dict should contain value :param proptyping: If the returned dict should contain value
that matches property type (e.g. max-players will be int) that matches property type (e.g. max-players will be int)
instead of string, defaults to True instead of string, defaults to True
:type proptyping: bool, optional :type proptyping: bool, optional
:return: Server.properties dict :return: Server.properties dict
:rtype: Dict[str,Any] :rtype: Dict[str,Any]
""" """
return self.__get_all_props('https://aternos.org/options', proptyping) return self.__get_all_props('https://aternos.org/options', proptyping)
def set_server_props(self, props:Dict[str,Any]) -> None: def set_server_props(self, props: Dict[str, Any]) -> None:
"""Updates server.properties options with the given dict """Updates server.properties options with the given dict
:param props: Dict with properties `{key:value}` :param props: Dict with properties `{key:value}`
:type props: Dict[str,Any] :type props: Dict[str,Any]
""" """
for key in props: for key in props:
self.set_server_prop(key, props[key]) self.set_server_prop(key, props[key])
# #
# level.dat # level.dat
# #
def set_world_prop( def set_world_prop(
self, option:Union[WorldOpts,WorldRules], self, option: Union[WorldOpts, WorldRules],
value:Any, gamerule:bool=False, value: Any, gamerule: bool = False,
world:str='world') -> None: world: str = 'world') -> None:
"""Sets level.dat option for specified world """Sets level.dat option for specified world
:param option: Option name :param option: Option name
:type option: Union[WorldOpts,WorldRules] :type option: Union[WorldOpts,WorldRules]
:param value: New value :param value: New value
:type value: Any :type value: Any
:param gamerule: If the option :param gamerule: If the option
is a gamerule, defaults to False is a gamerule, defaults to False
:type gamerule: bool, optional :type gamerule: bool, optional
:param world: Name of the world which :param world: Name of the world which
level.dat must be edited, defaults to 'world' level.dat must be edited, defaults to 'world'
:type world: str, optional :type world: str, optional
""" """
prefix = DAT_PREFIX prefix = DAT_PREFIX
if gamerule: if gamerule:
prefix = DAT_GR_PREFIX prefix = DAT_GR_PREFIX
self.__set_prop( self.__set_prop(
f'/{world}/level.dat', f'/{world}/level.dat',
f'{prefix}{option}', f'{prefix}{option}',
value value
) )
def get_world_props( def get_world_props(
self, world:str='world', self, world: str = 'world',
proptyping:bool=True) -> Dict[str,Any]: proptyping: bool = True) -> Dict[str, Any]:
"""Parses level.dat from specified world's options page """Parses level.dat from specified world's options page
:param world: Name of the world, defaults to 'world' :param world: Name of the world, defaults to 'world'
:type world: str, optional :type world: str, optional
:param proptyping: If the returned dict should contain the value :param proptyping: If the returned dict should contain the value
that matches property type (e.g. randomTickSpeed will be bool) that matches property type (e.g. randomTickSpeed will be bool)
instead of string, defaults to True instead of string, defaults to True
:type proptyping: bool, optional :type proptyping: bool, optional
:return: Level.dat dict :return: Level.dat dict
:rtype: Dict[str,Any] :rtype: Dict[str,Any]
""" """
self.__get_all_props( self.__get_all_props(
f'https://aternos.org/files/{world}/level.dat', f'https://aternos.org/files/{world}/level.dat',
proptyping, [DAT_PREFIX, DAT_GR_PREFIX] proptyping, [DAT_PREFIX, DAT_GR_PREFIX]
) )
def set_world_props(self, props:Dict[str,Any]) -> None: def set_world_props(self, props: Dict[str, Any]) -> None:
for key in props: for key in props:
self.set_world_prop(key, props[key]) self.set_world_prop(key, props[key])
# #
# helpers # helpers
# #
def __set_prop(self, file:str, option:str, value:Any) -> None: def __set_prop(self, file: str, option: str, value: Any) -> None:
self.atserv.atserver_request( self.atserv.atserver_request(
'https://aternos.org/panel/ajax/config.php', 'https://aternos.org/panel/ajax/config.php',
'POST', data={ 'POST', data={
'file': file, 'file': file,
'option': option, 'option': option,
'value': value 'value': value
}, sendtoken=True }, sendtoken=True
) )
def __get_all_props( def __get_all_props(
self, url:str, proptyping:bool=True, self, url: str, proptyping: bool = True,
prefixes:Optional[List[str]]=None) -> Dict[str,Any]: prefixes: Optional[List[str]] = None) -> Dict[str, Any]:
optreq = self.atserv.atserver_request(url, 'GET') optreq = self.atserv.atserver_request(url, 'GET')
opttree = lxml.html.fromstring(optreq.content) opttree = lxml.html.fromstring(optreq.content)
configs = opttree.xpath('//div[@class="config-options"]') configs = opttree.xpath('//div[@class="config-options"]')
for i, conf in enumerate(configs): for i, conf in enumerate(configs):
opts = conf.xpath('/div[contains(@class,"config-option ")]') opts = conf.xpath('/div[contains(@class,"config-option ")]')
result = {} result = {}
for opt in opts: for opt in opts:
key = opt.xpath('.//span[@class="config-option-output-key"]')[0].text key = opt.xpath(
value = opt.xpath('.//span[@class="config-option-output-value"]')[0].text './/span[@class="config-option-output-key"]'
)[0].text
value = opt.xpath(
'.//span[@class="config-option-output-value"]'
)[0].text
if prefixes != None: if prefixes is not None:
key = f'{prefixes[i]}{key}' key = f'{prefixes[i]}{key}'
opttype = opt.xpath('/@class').split(' ')[1] opttype = opt.xpath('/@class').split(' ')[1]
if proptyping and opttype in convert: if proptyping and opttype in convert:
value = convert[opttype](value) value = convert[opttype](value)
result[key] = value result[key] = value
return result return result

View file

@ -10,235 +10,242 @@ from .aterrors import TokenError, CloudflareError
REQUA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 OPR/85.0.4341.47' REQUA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 OPR/85.0.4341.47'
class AternosConnect: class AternosConnect:
""" """
Class for sending API requests bypass Cloudflare Class for sending API requests bypass Cloudflare
and parsing responses""" and parsing responses"""
def __init__(self) -> None: def __init__(self) -> None:
self.session = CloudScraper() self.session = CloudScraper()
self.atsession = '' self.atsession = ''
def parse_token(self) -> str: def parse_token(self) -> str:
"""Parses Aternos ajax token that """Parses Aternos ajax token that
is needed for most requests is needed for most requests
:raises RuntimeWarning: If the parser :raises RuntimeWarning: If the parser
can not find <head> tag in HTML response can not find <head> tag in HTML response
:raises CredentialsError: If the parser :raises CredentialsError: If the parser
is unable to extract ajax token in HTML is unable to extract ajax token in HTML
:return: Aternos ajax token :return: Aternos ajax token
:rtype: str :rtype: str
""" """
loginpage = self.request_cloudflare( loginpage = self.request_cloudflare(
f'https://aternos.org/go/', 'GET' f'https://aternos.org/go/', 'GET'
).content ).content
# Using the standard string methods # Using the standard string methods
# instead of the expensive xml parsing # instead of the expensive xml parsing
head = b'<head>' head = b'<head>'
headtag = loginpage.find(head) headtag = loginpage.find(head)
headend = loginpage.find(b'</head>', headtag + len(head)) headend = loginpage.find(b'</head>', headtag + len(head))
# Some checks # Some checks
if headtag < 0 or headend < 0: if headtag < 0 or headend < 0:
pagehead = loginpage pagehead = loginpage
raise RuntimeWarning('Unable to find <head> tag, parsing the whole page') raise RuntimeWarning(
'Unable to find <head> tag, parsing the whole page'
)
# Extracting <head> content # Extracting <head> content
headtag = headtag + len(head) headtag = headtag + len(head)
pagehead = loginpage[headtag:headend] pagehead = loginpage[headtag:headend]
try: try:
text = pagehead.decode('utf-8', 'replace') text = pagehead.decode('utf-8', 'replace')
js_code = re.findall(r'\(\(\)(.*?)\)\(\);', text) js_code = re.findall(r'\(\(\)(.*?)\)\(\);', text)
token_func = js_code[1] if len(js_code) > 1 else js_code[0] token_func = js_code[1] if len(js_code) > 1 else js_code[0]
ctx = atjsparse.exec(token_func) ctx = atjsparse.exec(token_func)
self.token = ctx.window['AJAX_TOKEN'] self.token = ctx.window['AJAX_TOKEN']
except (IndexError, TypeError): except (IndexError, TypeError):
raise TokenError( raise TokenError(
'Unable to parse TOKEN from the page' 'Unable to parse TOKEN from the page'
) )
return self.token return self.token
def generate_sec(self) -> str: def generate_sec(self) -> str:
"""Generates Aternos SEC token which """Generates Aternos SEC token which
is also needed for most API requests is also needed for most API requests
:return: Random SEC key:value string :return: Random SEC key:value string
:rtype: str :rtype: str
""" """
randkey = self.generate_aternos_rand() randkey = self.generate_aternos_rand()
randval = self.generate_aternos_rand() randval = self.generate_aternos_rand()
self.sec = f'{randkey}:{randval}' self.sec = f'{randkey}:{randval}'
self.session.cookies.set( self.session.cookies.set(
f'ATERNOS_SEC_{randkey}', randval, f'ATERNOS_SEC_{randkey}', randval,
domain='aternos.org' domain='aternos.org'
) )
return self.sec return self.sec
def generate_aternos_rand(self, randlen:int=16) -> str: def generate_aternos_rand(self, randlen: int = 16) -> str:
"""Generates a random string using """Generates a random string using
Aternos algorithm from main.js file Aternos algorithm from main.js file
:param randlen: Random string length, defaults to 16 :param randlen: Random string length, defaults to 16
:type randlen: int, optional :type randlen: int, optional
:return: Random string for SEC token :return: Random string for SEC token
:rtype: str :rtype: str
""" """
# a list with randlen+1 empty strings: # a list with randlen+1 empty strings:
# generate a string with spaces, # generate a string with spaces,
# then split it by space # then split it by space
rand_arr = (' ' * (randlen+1)).split(' ') rand_arr = (' ' * (randlen+1)).split(' ')
rand = random.random() rand = random.random()
rand_alphanum = self.convert_num(rand, 36) + ('0' * 17) rand_alphanum = self.convert_num(rand, 36) + ('0' * 17)
return (rand_alphanum[:18].join(rand_arr)[:randlen]) return (rand_alphanum[:18].join(rand_arr)[:randlen])
def convert_num( def convert_num(
self, num:Union[int,float,str], self, num: Union[int, float, str],
base:int, frombase:int=10) -> str: base: int, frombase: int = 10) -> str:
"""Converts an integer to specified base """Converts an integer to specified base
:param num: Integer in any base to convert. :param num: Integer in any base to convert.
If it is a float started with `0,`, If it is a float started with `0,`,
zero and comma will be removed to get int zero and comma will be removed to get int
:type num: Union[int,float,str] :type num: Union[int,float,str]
:param base: New base :param base: New base
:type base: int :type base: int
:param frombase: Given number base, defaults to 10 :param frombase: Given number base, defaults to 10
:type frombase: int, optional :type frombase: int, optional
:return: Number converted to a specified base :return: Number converted to a specified base
:rtype: str :rtype: str
""" """
if isinstance(num, str): if isinstance(num, str):
num = int(num, frombase) num = int(num, frombase)
if isinstance(num, float): if isinstance(num, float):
sliced = str(num)[2:] sliced = str(num)[2:]
num = int(sliced) num = int(sliced)
symbols = '0123456789abcdefghijklmnopqrstuvwxyz' symbols = '0123456789abcdefghijklmnopqrstuvwxyz'
basesym = symbols[:base] basesym = symbols[:base]
result = '' result = ''
while num > 0: while num > 0:
rem = num % base rem = num % base
result = str(basesym[rem]) + result result = str(basesym[rem]) + result
num //= base num //= base
return result return result
def request_cloudflare( def request_cloudflare(
self, url:str, method:str, self, url: str, method: str,
params:Optional[dict]=None, data:Optional[dict]=None, params: Optional[dict] = None,
headers:Optional[dict]=None, reqcookies:Optional[dict]=None, data: Optional[dict] = None,
sendtoken:bool=False, redirect:bool=True, retry:int=3) -> Response: headers: Optional[dict] = None,
reqcookies: Optional[dict] = None,
sendtoken: bool = False,
redirect: bool = True,
retry: int = 3) -> Response:
"""Sends a request to Aternos API bypass Cloudflare """Sends a request to Aternos API bypass Cloudflare
:param url: Request URL :param url: Request URL
:type url: str :type url: str
:param method: Request method, must be GET or POST :param method: Request method, must be GET or POST
:type method: str :type method: str
:param params: URL parameters, defaults to None :param params: URL parameters, defaults to None
:type params: Optional[dict], optional :type params: Optional[dict], optional
:param data: POST request data, if the method is GET, :param data: POST request data, if the method is GET,
this dict will be combined with params, defaults to None this dict will be combined with params, defaults to None
:type data: Optional[dict], optional :type data: Optional[dict], optional
:param headers: Custom headers, defaults to None :param headers: Custom headers, defaults to None
:type headers: Optional[dict], optional :type headers: Optional[dict], optional
:param reqcookies: Cookies only for this request, defaults to None :param reqcookies: Cookies only for this request, defaults to None
:type reqcookies: Optional[dict], optional :type reqcookies: Optional[dict], optional
:param sendtoken: If the ajax and SEC token :param sendtoken: If the ajax and SEC token
should be sent, defaults to False should be sent, defaults to False
:type sendtoken: bool, optional :type sendtoken: bool, optional
:param redirect: If requests lib should follow :param redirect: If requests lib should follow
Location header in 3xx responses, defaults to True Location header in 3xx responses, defaults to True
:type redirect: bool, optional :type redirect: bool, optional
:param retry: How many times parser must retry :param retry: How many times parser must retry
connection to API bypass Cloudflare, defaults to 3 connection to API bypass Cloudflare, defaults to 3
:type retry: int, optional :type retry: int, optional
:raises CloudflareError: :raises CloudflareError:
When the parser has exceeded retries count When the parser has exceeded retries count
:raises NotImplementedError: :raises NotImplementedError:
When the specified method is not GET or POST When the specified method is not GET or POST
:return: API response :return: API response
:rtype: requests.Response :rtype: requests.Response
""" """
if retry <= 0: if retry <= 0:
raise CloudflareError('Unable to bypass Cloudflare protection') raise CloudflareError('Unable to bypass Cloudflare protection')
try: try:
self.atsession = self.session.cookies['ATERNOS_SESSION'] self.atsession = self.session.cookies['ATERNOS_SESSION']
except KeyError: except KeyError:
pass pass
method = method or 'GET' method = method or 'GET'
method = method.upper().strip() method = method.upper().strip()
if method not in ('GET', 'POST'): if method not in ('GET', 'POST'):
raise NotImplementedError('Only GET and POST are available') raise NotImplementedError('Only GET and POST are available')
headers = headers or {} headers = headers or {}
params = params or {} params = params or {}
data = data or {} data = data or {}
reqcookies = reqcookies or {} reqcookies = reqcookies or {}
if sendtoken: if sendtoken:
params['TOKEN'] = self.token params['TOKEN'] = self.token
params['SEC'] = self.sec params['SEC'] = self.sec
headers['X-Requested-With'] = 'XMLHttpRequest' headers['X-Requested-With'] = 'XMLHttpRequest'
# requests.cookies.CookieConflictError bugfix # requests.cookies.CookieConflictError bugfix
reqcookies['ATERNOS_SESSION'] = self.atsession reqcookies['ATERNOS_SESSION'] = self.atsession
del self.session.cookies['ATERNOS_SESSION'] del self.session.cookies['ATERNOS_SESSION']
logging.debug(f'Requesting({method})' + url) logging.debug(f'Requesting({method})' + url)
logging.debug('headers=' + str(headers)) logging.debug('headers=' + str(headers))
logging.debug('params=' + str(params)) logging.debug('params=' + str(params))
logging.debug('data=' + str(data)) logging.debug('data=' + str(data))
logging.debug('req-cookies=' + str(reqcookies)) logging.debug('req-cookies=' + str(reqcookies))
logging.debug('session-cookies=' + str(self.session.cookies)) logging.debug('session-cookies=' + str(self.session.cookies))
if method == 'POST': if method == 'POST':
req = self.session.post( req = self.session.post(
url, data=data, params=params, url, data=data, params=params,
headers=headers, cookies=reqcookies, headers=headers, cookies=reqcookies,
allow_redirects=redirect allow_redirects=redirect
) )
else: else:
req = self.session.get( req = self.session.get(
url, params={**params, **data}, url, params={**params, **data},
headers=headers, cookies=reqcookies, headers=headers, cookies=reqcookies,
allow_redirects=redirect allow_redirects=redirect
) )
if '<title>Please Wait... | Cloudflare</title>' in req.text: if '<title>Please Wait... | Cloudflare</title>' in req.text:
logging.info('Retrying to bypass Cloudflare') logging.info('Retrying to bypass Cloudflare')
self.request_cloudflare( self.request_cloudflare(
url, method, url, method,
params, data, params, data,
headers, reqcookies, headers, reqcookies,
sendtoken, redirect, sendtoken, redirect,
retry - 1 retry - 1
) )
logging.info( logging.info(
f'{method} completed with {req.status_code} status' f'{method} completed with {req.status_code} status'
) )
return req return req

View file

@ -1,39 +1,56 @@
class AternosError(Exception): class AternosError(Exception):
"""Common error class""" """Common error class"""
class CloudflareError(AternosError): class CloudflareError(AternosError):
"""Raised when the parser is unable to bypass Cloudflare protection""" """Raised when the parser is unable
to bypass Cloudflare protection"""
class CredentialsError(AternosError): class CredentialsError(AternosError):
"""Raised when a session cookie is empty which means incorrect credentials""" """Raised when a session cookie is empty
which means incorrect credentials"""
class TokenError(AternosError): class TokenError(AternosError):
"""Raised when the parser is unable to extract Aternos ajax token""" """Raised when the parser is unable
to extract Aternos ajax token"""
class ServerError(AternosError): class ServerError(AternosError):
"""Common class for server errors""" """Common class for server errors"""
class ServerEulaError(ServerError): class ServerEulaError(ServerError):
"""Raised when trying to start without confirming Mojang EULA""" """Raised when trying to start without
confirming Mojang EULA"""
class ServerRunningError(ServerError): class ServerRunningError(ServerError):
"""Raised when trying to start already running server""" """Raised when trying to start
already running server"""
class ServerSoftwareError(ServerError): class ServerSoftwareError(ServerError):
"""Raised when Aternos notifies about incorrect software version""" """Raised when Aternos notifies about
incorrect software version"""
class ServerStorageError(ServerError): class ServerStorageError(ServerError):
"""Raised when Aternos notifies about violation of storage limits (4 GB for now)""" """Raised when Aternos notifies about
violation of storage limits (4 GB for now)"""
class FileError(AternosError): class FileError(AternosError):
"""Raised when trying to execute a disallowed by Aternos file operation""" """Raised when trying to execute a disallowed
by Aternos file operation"""

View file

@ -6,142 +6,146 @@ from typing import TYPE_CHECKING
from .aterrors import FileError from .aterrors import FileError
if TYPE_CHECKING: if TYPE_CHECKING:
from .atserver import AternosServer from .atserver import AternosServer
class FileType(enum.IntEnum): class FileType(enum.IntEnum):
"""File or dierctory""" """File or dierctory"""
file = 0
directory = 1
file = 0
directory = 1
class AternosFile: class AternosFile:
"""File class which contains info about its path, type and size """File class which contains info about its path, type and size
:param atserv: :class:`python_aternos.atserver.AternosServer` instance :param atserv: :class:`python_aternos.atserver.AternosServer` instance
:type atserv: python_aternos.atserver.AternosServer :type atserv: python_aternos.atserver.AternosServer
:param path: Path to the file :param path: Path to the file
:type path: str :type path: str
:param name: Filename :param name: Filename
:type name: str :type name: str
:param ftype: File or directory :param ftype: File or directory
:type ftype: python_aternos.atfile.FileType :type ftype: python_aternos.atfile.FileType
:param size: File size, defaults to 0 :param size: File size, defaults to 0
:type size: Union[int,float], optional :type size: Union[int,float], optional
""" """
def __init__( def __init__(
self, atserv:'AternosServer', self,
path:str, name:str, ftype:FileType=FileType.file, atserv: 'AternosServer',
size:Union[int,float]=0) -> None: path: str, name: str,
ftype: FileType = FileType.file,
size: Union[int, float] = 0) -> None:
self.atserv = atserv self.atserv = atserv
self._path = path.lstrip('/') self._path = path.lstrip('/')
self._name = name self._name = name
self._full = path + name self._full = path + name
self._ftype = ftype self._ftype = ftype
self._size = float(size) self._size = float(size)
def delete(self) -> None: def delete(self) -> None:
"""Deletes the file""" """Deletes the file"""
self.atserv.atserver_request( self.atserv.atserver_request(
'https://aternos.org/panel/ajax/delete.php', 'https://aternos.org/panel/ajax/delete.php',
'POST', data={'file': self._full}, 'POST', data={'file': self._full},
sendtoken=True sendtoken=True
) )
def get_content(self) -> bytes: def get_content(self) -> bytes:
"""Requests file content in bytes (downloads it) """Requests file content in bytes (downloads it)
:raises FileError: If downloading :raises FileError: If downloading
the file is not allowed by Aternos the file is not allowed by Aternos
:return: File content :return: File content
:rtype: bytes :rtype: bytes
""" """
file = self.atserv.atserver_request( file = self.atserv.atserver_request(
'https://aternos.org/panel/ajax/files/download.php', 'https://aternos.org/panel/ajax/files/download.php',
'GET', params={ 'GET', params={
'file': self._full 'file': self._full
} }
) )
if file.content == b'{"success":false}': if file.content == b'{"success":false}':
raise FileError('Unable to download the file. Try to get text') raise FileError('Unable to download the file. Try to get text')
return file.content return file.content
def set_content(self, value:bytes) -> None: def set_content(self, value: bytes) -> None:
"""Modifies the file content """Modifies the file content
:param value: New content :param value: New content
:type value: bytes :type value: bytes
""" """
self.atserv.atserver_request( self.atserv.atserver_request(
f'https://aternos.org/panel/ajax/save.php', f'https://aternos.org/panel/ajax/save.php',
'POST', data={ 'POST', data={
'file': self._full, 'file': self._full,
'content': value 'content': value
}, sendtoken=True }, sendtoken=True
) )
def get_text(self) -> str: def get_text(self) -> str:
"""Requests editing the file as a text """Requests editing the file as a text
(try it if downloading is disallowed) (try it if downloading is disallowed)
:return: File text content :return: File text content
:rtype: str :rtype: str
""" """
editor = self.atserv.atserver_request( editor = self.atserv.atserver_request(
f'https://aternos.org/files/{self._full.lstrip("/")}', 'GET' f'https://aternos.org/files/{self._full.lstrip("/")}', 'GET'
) )
edittree = lxml.html.fromstring(editor.content) edittree = lxml.html.fromstring(editor.content)
editblock = edittree.xpath('//div[@id="editor"]')[0] editblock = edittree.xpath('//div[@id="editor"]')[0]
return editblock.text_content() return editblock.text_content()
def set_text(self, value:str) -> None: def set_text(self, value: str) -> None:
"""Modifies the file content, """Modifies the file content,
but unlike set_content takes but unlike set_content takes
a string as a new value a string as a new value
:param value: New content :param value: New content
:type value: str :type value: str
""" """
self.set_content(value.encode('utf-8')) self.set_content(value.encode('utf-8'))
@property @property
def path(self): def path(self):
return self._path return self._path
@property @property
def name(self) -> str: def name(self) -> str:
return self._name return self._name
@property @property
def full(self) -> str: def full(self) -> str:
return self._full return self._full
@property @property
def is_dir(self) -> bool: def is_dir(self) -> bool:
if self._ftype == FileType.directory: if self._ftype == FileType.directory:
return True return True
return False return False
@property @property
def is_file(self) -> bool: def is_file(self) -> bool:
if self._ftype == FileType.file: if self._ftype == FileType.file:
return True return True
return False return False
@property @property
def size(self) -> float: def size(self) -> float:
return self._size return self._size

View file

@ -4,150 +4,155 @@ from typing import TYPE_CHECKING
from .atfile import AternosFile, FileType from .atfile import AternosFile, FileType
if TYPE_CHECKING: if TYPE_CHECKING:
from .atserver import AternosServer from .atserver import AternosServer
class FileManager: class FileManager:
"""Aternos file manager class for viewing files structure """Aternos file manager class for viewing files structure
:param atserv: :class:`python_aternos.atserver.AternosServer` instance :param atserv: :class:`python_aternos.atserver.AternosServer` instance
:type atserv: python_aternos.atserver.AternosServer :type atserv: python_aternos.atserver.AternosServer
""" """
def __init__(self, atserv:'AternosServer') -> None: def __init__(self, atserv: 'AternosServer') -> None:
self.atserv = atserv self.atserv = atserv
def listdir(self, path:str='') -> List[AternosFile]: def listdir(self, path: str = '') -> List[AternosFile]:
"""Requests a list of files """Requests a list of files
in the specified directory in the specified directory
:param path: Directory :param path: Directory
(an empty string means root), defaults to '' (an empty string means root), defaults to ''
:type path: str, optional :type path: str, optional
:return: List of :class:`python_aternos.atfile.AternosFile` :return: List of :class:`python_aternos.atfile.AternosFile`
:rtype: List[AternosFile] :rtype: List[AternosFile]
""" """
path = path.lstrip('/') path = path.lstrip('/')
filesreq = self.atserv.atserver_request( filesreq = self.atserv.atserver_request(
f'https://aternos.org/files/{path}', 'GET' f'https://aternos.org/files/{path}', 'GET'
) )
filestree = lxml.html.fromstring(filesreq.content) filestree = lxml.html.fromstring(filesreq.content)
fileslist = filestree.xpath('//div[contains(concat(" ",normalize-space(@class)," ")," file ")]') fileslist = filestree.xpath(
'//div[contains(concat(" ",normalize-space(@class)," ")," file ")]'
)
files = [] files = []
for f in fileslist: for f in fileslist:
ftype_raw = f.xpath('@data-type')[0] ftype_raw = f.xpath('@data-type')[0]
ftype = FileType.file \ ftype = FileType.file \
if ftype_raw == 'file' \ if ftype_raw == 'file' \
else FileType.directory else FileType.directory
fsize_raw = f.xpath('./div[@class="filesize"]') fsize_raw = f.xpath('./div[@class="filesize"]')
fsize = 0 fsize = 0
if len(fsize_raw) > 0: if len(fsize_raw) > 0:
fsize_text = fsize_raw[0].text.strip() fsize_text = fsize_raw[0].text.strip()
fsize_num = fsize_text[:fsize_text.rfind(' ')] fsize_num = fsize_text[:fsize_text.rfind(' ')]
fsize_msr = fsize_text[fsize_text.rfind(' ')+1:] fsize_msr = fsize_text[fsize_text.rfind(' ')+1:]
try: try:
fsize = self.convert_size(float(fsize_num), fsize_msr) fsize = self.convert_size(float(fsize_num), fsize_msr)
except ValueError: except ValueError:
fsize = -1 fsize = -1
fullpath = f.xpath('@data-path')[0] fullpath = f.xpath('@data-path')[0]
filepath = fullpath[:fullpath.rfind('/')] filepath = fullpath[:fullpath.rfind('/')]
filename = fullpath[fullpath.rfind('/'):] filename = fullpath[fullpath.rfind('/'):]
files.append( files.append(
AternosFile( AternosFile(
self.atserv, self.atserv,
filepath, filename, filepath, filename,
ftype, fsize ftype, fsize
) )
) )
return files return files
def convert_size(self, num:Union[int,float], measure:str) -> float: def convert_size(self, num: Union[int, float], measure: str) -> float:
"""Converts "human" file size to size in bytes """Converts "human" file size to size in bytes
:param num: Size :param num: Size
:type num: Union[int,float] :type num: Union[int,float]
:param measure: Units (B, kB, MB, GB) :param measure: Units (B, kB, MB, GB)
:type measure: str :type measure: str
:return: Size in bytes :return: Size in bytes
:rtype: float :rtype: float
""" """
measure_match = { measure_match = {
'B': 1, 'B': 1,
'kB': 1000, 'kB': 1000,
'MB': 1000000, 'MB': 1000000,
'GB': 1000000000 'GB': 1000000000
} }
try: try:
return num * measure_match[measure] return num * measure_match[measure]
except KeyError: except KeyError:
return -1 return -1
def get_file(self, path:str) -> Optional[AternosFile]: def get_file(self, path: str) -> Optional[AternosFile]:
"""Returns :class:`python_aternos.atfile.AternosFile` """Returns :class:`python_aternos.atfile.AternosFile`
instance by its path instance by its path
:param path: Path to file including its filename :param path: Path to file including its filename
:type path: str :type path: str
:return: _description_ :return: _description_
:rtype: Optional[AternosFile] :rtype: Optional[AternosFile]
""" """
filepath = path[:path.rfind('/')] filepath = path[:path.rfind('/')]
filename = path[path.rfind('/'):] filename = path[path.rfind('/'):]
filedir = self.listdir(filepath) filedir = self.listdir(filepath)
for file in filedir: for file in filedir:
if file.name == filename: if file.name == filename:
return file return file
return None return None
def dl_file(self, path:str) -> bytes: def dl_file(self, path: str) -> bytes:
"""Returns the file content in bytes (downloads it) """Returns the file content in bytes (downloads it)
:param path: Path to file including its filename :param path: Path to file including its filename
:type path: str :type path: str
:return: File content :return: File content
:rtype: bytes :rtype: bytes
""" """
file = self.atserv.atserver_request( file = self.atserv.atserver_request(
f'https://aternos.org/panel/ajax/files/download.php?' + \ 'https://aternos.org/panel/ajax/files/download.php'
f'file={path.replace("/","%2F")}', 'GET', params={
'GET' 'file': path.replace('/', '%2F')
) }
)
return file.content return file.content
def dl_world(self, world:str='world') -> bytes: def dl_world(self, world: str = 'world') -> bytes:
"""Returns the world zip file content """Returns the world zip file content
by its name (downloads it) by its name (downloads it)
:param world: Name of world, defaults to 'world' :param world: Name of world, defaults to 'world'
:type world: str, optional :type world: str, optional
:return: Zip file content :return: Zip file content
:rtype: bytes :rtype: bytes
""" """
world = self.atserv.atserver_request( world = self.atserv.atserver_request(
f'https://aternos.org/panel/ajax/worlds/download.php?' + \ 'https://aternos.org/panel/ajax/worlds/download.php'
f'world={world.replace("/","%2F")}', 'GET', params={
'GET' 'world': world.replace('/', '%2F')
) }
)
return world.content return world.content

View file

@ -6,42 +6,45 @@ from typing import Any
# Thanks to http://regex.inginf.units.it/ # Thanks to http://regex.inginf.units.it/
arrowexp = regex.compile(r'\w[^\}]*+') arrowexp = regex.compile(r'\w[^\}]*+')
def to_ecma5_function(f:str) -> str:
"""Converts a ECMA6 function to ECMA5 format (without arrow expressions) def to_ecma5_function(f: str) -> str:
:param f: ECMA6 function """Converts a ECMA6 function to ECMA5 format (without arrow expressions)
:type f: str
:return: ECMA5 function
:rtype: str
"""
match = arrowexp.search(f) :param f: ECMA6 function
conv = '(function(){' + match.group(0) + '})()' :type f: str
return regex.sub( :return: ECMA5 function
r'(?:s|\(s\)) => s.split\([\'"]{2}\).reverse\(\).join\([\'"]{2}\)', :rtype: str
'function(s){return s.split(\'\').reverse().join(\'\')}', """
conv
)
def atob(s:str) -> str: match = arrowexp.search(f)
return base64.standard_b64decode(str(s)).decode('utf-8') conv = '(function(){' + match.group(0) + '})()'
return regex.sub(
r'(?:s|\(s\)) => s.split\([\'"]{2}\).reverse\(\).join\([\'"]{2}\)',
'function(s){return s.split(\'\').reverse().join(\'\')}',
conv
)
def exec(f:str) -> Any:
"""Executes a JavaScript function def atob(s: str) -> str:
return base64.standard_b64decode(str(s)).decode('utf-8')
:param f: ECMA6 function
:type f: str
:return: JavaScript interpreter context
:rtype: Any
"""
ctx = js2py.EvalJs({'atob': atob}) def exec(f: str) -> Any:
ctx.execute('window.document = { };')
ctx.execute('window.Map = function(_i){ };') """Executes a JavaScript function
ctx.execute('window.setTimeout = function(_f,_t){ };')
ctx.execute('window.setInterval = function(_f,_t){ };') :param f: ECMA6 function
ctx.execute('window.encodeURIComponent = function(_s){ };') :type f: str
ctx.execute(to_ecma5_function(f)) :return: JavaScript interpreter context
return ctx :rtype: Any
"""
ctx = js2py.EvalJs({'atob': atob})
ctx.execute('window.document = { };')
ctx.execute('window.Map = function(_i){ };')
ctx.execute('window.setTimeout = function(_f,_t){ };')
ctx.execute('window.setInterval = function(_f,_t){ };')
ctx.execute('window.encodeURIComponent = function(_s){ };')
ctx.execute(to_ecma5_function(f))
return ctx

View file

@ -4,98 +4,103 @@ from typing import List, Union
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from .atserver import AternosServer from .atserver import AternosServer
class Lists(enum.Enum): class Lists(enum.Enum):
whl = 'whitelist' """Players list type enum"""
ops = 'ops'
ban = 'banned-players' whl = 'whitelist'
ips = 'banned-ips' ops = 'ops'
ban = 'banned-players'
ips = 'banned-ips'
class PlayersList: class PlayersList:
"""Class for managing operators, whitelist and banned players lists """Class for managing operators, whitelist and banned players lists
:param lst: Players list type, must be :param lst: Players list type, must be
:class:`python_aternos.atplayers.Lists` enum value :class:`python_aternos.atplayers.Lists` enum value
:type lst: Union[str,Lists] :type lst: Union[str,Lists]
:param atserv: :class:`python_aternos.atserver.AternosServer` instance :param atserv: :class:`python_aternos.atserver.AternosServer` instance
:type atserv: python_aternos.atserver.AternosServer :type atserv: python_aternos.atserver.AternosServer
""" """
def __init__(self, lst:Union[str,Lists], atserv:'AternosServer') -> None: def __init__(self, lst: Union[str, Lists], atserv: 'AternosServer') -> None:
self.atserv = atserv self.atserv = atserv
self.lst = Lists(lst) self.lst = Lists(lst)
self.players = [] self.players = []
self.parsed = False self.parsed = False
def list_players(self, cache:bool=True) -> List[str]: def list_players(self, cache: bool = True) -> List[str]:
"""Parse a players list """Parse a players list
:param cache: If the function can return cached list (highly recommended), defaults to True :param cache: If the function can return
:type cache: bool, optional cached list (highly recommended), defaults to True
:return: List of players nicknames :type cache: bool, optional
:rtype: List[str] :return: List of players nicknames
""" :rtype: List[str]
"""
if cache and self.parsed: if cache and self.parsed:
return self.players return self.players
listreq = self.atserv.atserver_request( listreq = self.atserv.atserver_request(
f'https://aternos.org/players/{self.lst.value}', f'https://aternos.org/players/{self.lst.value}',
'GET' 'GET'
) )
listtree = lxml.html.fromstring(listreq.content) listtree = lxml.html.fromstring(listreq.content)
items = listtree.xpath( items = listtree.xpath(
'//div[@class="list-item"]' '//div[@class="list-item"]'
) )
result = [] result = []
for i in items: for i in items:
name = i.xpath('./div[@class="list-name"]') name = i.xpath('./div[@class="list-name"]')
result.append(name[0].text.strip()) result.append(name[0].text.strip())
self.players = result self.players = result
self.parsed = True self.parsed = True
return result return result
def add(self, name:str) -> None: def add(self, name: str) -> None:
"""Appends a player to the list by the nickname """Appends a player to the list by the nickname
:param name: Player's nickname :param name: Player's nickname
:type name: str :type name: str
""" """
self.atserv.atserver_request( self.atserv.atserver_request(
'https://aternos.org/panel/ajax/players/add.php', 'https://aternos.org/panel/ajax/players/add.php',
'POST', data={ 'POST', data={
'list': self.lst.value, 'list': self.lst.value,
'name': name 'name': name
}, sendtoken=True }, sendtoken=True
) )
self.players.append(name) self.players.append(name)
def remove(self, name:str) -> None: def remove(self, name: str) -> None:
"""Removes a player from the list by the nickname """Removes a player from the list by the nickname
:param name: Player's nickname :param name: Player's nickname
:type name: str :type name: str
""" """
self.atserv.atserver_request( self.atserv.atserver_request(
'https://aternos.org/panel/ajax/players/remove.php', 'https://aternos.org/panel/ajax/players/remove.php',
'POST', data={ 'POST', data={
'list': self.lst.value, 'list': self.lst.value,
'name': name 'name': name
}, sendtoken=True }, sendtoken=True
) )
for i, j in enumerate(self.players): for i, j in enumerate(self.players):
if j == name: if j == name:
del self.players[i] del self.players[i]

View file

@ -15,329 +15,337 @@ from .atplayers import PlayersList
from .atplayers import Lists from .atplayers import Lists
from .atwss import AternosWss from .atwss import AternosWss
class Edition(enum.IntEnum): class Edition(enum.IntEnum):
"""Server edition type enum""" """Server edition type enum"""
java = 0
bedrock = 1
java = 0
bedrock = 1
class Status(enum.IntEnum): class Status(enum.IntEnum):
"""Server numeric status enum. """Server numeric status enum.
It is highly recommended to use It is highly recommended to use
`AternosServer.status` instead of `AternosServer.status` instead of
`AternosServer.status_num`""" `AternosServer.status_num`"""
off = 0
on = 1
starting = 2
shutdown = 3
unknown = 6
error = 7
confirm = 10
off = 0
on = 1
starting = 2
shutdown = 3
unknown = 6
error = 7
confirm = 10
class AternosServer: class AternosServer:
"""Class for controlling your Aternos Minecraft server """Class for controlling your Aternos Minecraft server
:param servid: Unique server IDentifier :param servid: Unique server IDentifier
:type servid: str :type servid: str
:param atconn: :class:`python_aternos.atconnect.AternosConnect` :param atconn: :class:`python_aternos.atconnect.AternosConnect`
instance with initialized Aternos session instance with initialized Aternos session
:type atconn: python_aternos.atconnect.AternosConnect :type atconn: python_aternos.atconnect.AternosConnect
:param reqinfo: Automatically call AternosServer.fetch() to get all info, defaults to `True` :param reqinfo: Automatically call AternosServer.fetch()
:type reqinfo: bool, optional to get all info, defaults to `True`
""" :type reqinfo: bool, optional
"""
def __init__(
self, servid:str, def __init__(
atconn:AternosConnect, self, servid: str,
reqinfo:bool=True) -> None: atconn: AternosConnect,
reqinfo: bool = True) -> None:
self.servid = servid
self.atconn = atconn self.servid = servid
if reqinfo: self.atconn = atconn
self.fetch() if reqinfo:
self.fetch()
def fetch(self) -> None:
def fetch(self) -> None:
"""Send a request to Aternos API to get all server info"""
"""Send a request to Aternos API to get all server info"""
servreq = self.atserver_request(
'https://aternos.org/panel/ajax/status.php', servreq = self.atserver_request(
'GET', sendtoken=True 'https://aternos.org/panel/ajax/status.php',
) 'GET', sendtoken=True
self._info = json.loads(servreq.content) )
self._info = json.loads(servreq.content)
def wss(self, autoconfirm:bool=False) -> AternosWss:
def wss(self, autoconfirm: bool = False) -> AternosWss:
"""Returns :class:`python_aternos.atwss.AternosWss` instance for listening server streams in real-time
"""Returns :class:`python_aternos.atwss.AternosWss`
:param autoconfirm: Automatically start server status listener instance for listening server streams in real-time
when AternosWss connects to API to confirm server launching, defaults to `False`
:type autoconfirm: bool, optional :param autoconfirm: Automatically start server status listener
:return: :class:`python_aternos.atwss.AternosWss` object when AternosWss connects to API to confirm
:rtype: python_aternos.atwss.AternosWss server launching, defaults to `False`
""" :type autoconfirm: bool, optional
:return: :class:`python_aternos.atwss.AternosWss` object
return AternosWss(self, autoconfirm) :rtype: python_aternos.atwss.AternosWss
"""
def start(self, headstart:bool=False, accepteula:bool=True) -> None:
return AternosWss(self, autoconfirm)
"""Starts a server
def start(self, headstart: bool = False, accepteula: bool = True) -> None:
:param headstart: Start a server in the headstart mode which allows you to skip all queue, defaults to `False`
:type headstart: bool, optional """Starts a server
:param accepteula: Automatically accept the Mojang EULA, defaults to `True`
:type accepteula: bool, optional :param headstart: Start a server in the headstart mode
:raises ServerEulaError: When trying to start a server which allows you to skip all queue, defaults to `False`
without accepting the Mojang EULA :type headstart: bool, optional
:raises ServerRunningError: When trying to start a server :param accepteula: Automatically accept
which is alreday running the Mojang EULA, defaults to `True`
:raises ServerSoftwareError: When Aternos notifies about :type accepteula: bool, optional
incorrect software version :raises ServerEulaError: When trying to start a server
:raises ServerStorageError: When Aternos notifies about without accepting the Mojang EULA
voilation of storage limits (4 GB for now) :raises ServerRunningError: When trying to start a server
:raises ServerError: When API is unable to start a Minecraft server which is alreday running
due to unavailability of Aternos' file servers or other problems :raises ServerSoftwareError: When Aternos notifies about
""" incorrect software version
:raises ServerStorageError: When Aternos notifies about
voilation of storage limits (4 GB for now)
:raises ServerError: When API is unable to start a Minecraft server
due to unavailability of Aternos' file servers or other problems
"""
startreq = self.atserver_request(
'https://aternos.org/panel/ajax/start.php',
'GET', params={'headstart': int(headstart)},
sendtoken=True
)
startresult = startreq.json()
startreq = self.atserver_request( if startresult['success']:
'https://aternos.org/panel/ajax/start.php', return
'GET', params={'headstart': int(headstart)}, error = startresult['error']
sendtoken=True
)
startresult = startreq.json()
if startresult['success']: if error == 'eula' and accepteula:
return self.eula()
error = startresult['error'] self.start(accepteula=False)
if error == 'eula' and accepteula: elif error == 'eula':
self.eula() raise ServerEulaError(
self.start(accepteula=False) 'EULA was not accepted. Use start(accepteula=True)'
)
elif error == 'eula': elif error == 'already':
raise ServerEulaError( raise ServerRunningError(
'EULA was not accepted. Use start(accepteula=True)' 'Server is already running'
) )
elif error == 'already': elif error == 'wrongversion':
raise ServerRunningError( raise ServerSoftwareError(
'Server is already running' 'Incorrect software version installed'
) )
elif error == 'wrongversion': elif error == 'file':
raise ServerSoftwareError( raise ServerError(
'Incorrect software version installed' 'File server is unavailbale, view https://status.aternos.gmbh'
) )
elif error == 'file': elif error == 'size':
raise ServerError( raise ServerStorageError(
'File server is unavailbale, view https://status.aternos.gmbh' f'Available storage size is 4GB, '
) f'your server used: {startresult["size"]}'
)
elif error == 'size': else:
raise ServerStorageError( raise ServerError(
f'Available storage size is 4GB, ' + \ f'Unable to start server, code: {error}'
f'your server used: {startresult["size"]}' )
)
else: def confirm(self) -> None:
raise ServerError(
f'Unable to start server, code: {error}'
)
def confirm(self) -> None: """Confirms server launching"""
"""Confirms server launching""" self.atserver_request(
'https://aternos.org/panel/ajax/confirm.php',
self.atserver_request( 'GET', sendtoken=True
'https://aternos.org/panel/ajax/confirm.php', )
'GET', sendtoken=True
) def stop(self) -> None:
def stop(self) -> None: """Stops the server"""
"""Stops the server""" self.atserver_request(
'https://aternos.org/panel/ajax/stop.php',
self.atserver_request( 'GET', sendtoken=True
'https://aternos.org/panel/ajax/stop.php', )
'GET', sendtoken=True
) def cancel(self) -> None:
def cancel(self) -> None: """Cancels server launching"""
"""Cancels server launching""" self.atserver_request(
'https://aternos.org/panel/ajax/cancel.php',
self.atserver_request( 'GET', sendtoken=True
'https://aternos.org/panel/ajax/cancel.php', )
'GET', sendtoken=True
) def restart(self) -> None:
def restart(self) -> None: """Restarts the server"""
"""Restarts the server""" self.atserver_request(
'https://aternos.org/panel/ajax/restart.php',
self.atserver_request( 'GET', sendtoken=True
'https://aternos.org/panel/ajax/restart.php', )
'GET', sendtoken=True
) def eula(self) -> None:
def eula(self) -> None: """Accepts the Mojang EULA"""
"""Accepts the Mojang EULA""" self.atserver_request(
'https://aternos.org/panel/ajax/eula.php',
self.atserver_request( 'GET', sendtoken=True
'https://aternos.org/panel/ajax/eula.php', )
'GET', sendtoken=True
) def files(self) -> FileManager:
def files(self) -> FileManager: """Returns :class:`python_aternos.atfm.FileManager`
instance for file operations
"""Returns :class:`python_aternos.atfm.FileManager`
instance for file operations :return: :class:`python_aternos.atfm.FileManager` object
:rtype: python_aternos.atfm.FileManager
:return: :class:`python_aternos.atfm.FileManager` object """
:rtype: python_aternos.atfm.FileManager
""" return FileManager(self)
return FileManager(self) def config(self) -> AternosConfig:
def config(self) -> AternosConfig: """Returns :class:`python_aternos.atconf.AternosConfig`
instance for editing server settings
"""Returns :class:`python_aternos.atconf.AternosConfig`
instance for editing server settings :return: :class:`python_aternos.atconf.AternosConfig` object
:rtype: python_aternos.atconf.AternosConfig
:return: :class:`python_aternos.atconf.AternosConfig` object """
:rtype: python_aternos.atconf.AternosConfig
""" return AternosConfig(self)
return AternosConfig(self) def players(self, lst: Lists) -> PlayersList:
def players(self, lst:Lists) -> PlayersList: """Returns :class:`python_aternos.atplayers.PlayersList`
instance for managing operators, whitelist and banned players lists
"""Returns :class:`python_aternos.atplayers.PlayersList`
instance for managing operators, whitelist and banned players lists :param lst: Players list type, must be
the :class:`python_aternos.atplayers.Lists` enum value
:param lst: Players list type, must be :type lst: python_aternos.atplayers.Lists
the :class:`python_aternos.atplayers.Lists` enum value :return: :class:`python_aternos.atplayers.PlayersList`
:type lst: python_aternos.atplayers.Lists :rtype: python_aternos.atplayers.PlayersList
:return: :class:`python_aternos.atplayers.PlayersList` """
:rtype: python_aternos.atplayers.PlayersList
""" return PlayersList(lst, self)
return PlayersList(lst, self) def atserver_request(
self, url: str, method: str,
def atserver_request( params: Optional[dict] = None,
self, url:str, method:str, data: Optional[dict] = None,
params:Optional[dict]=None, headers: Optional[dict] = None,
data:Optional[dict]=None, sendtoken: bool = False) -> Response:
headers:Optional[dict]=None,
sendtoken:bool=False) -> Response: """Sends a request to Aternos API
with server IDenitfier parameter
"""Sends a request to Aternos API
with server IDenitfier parameter :param url: Request URL
:type url: str
:param url: Request URL :param method: Request method, must be GET or POST
:type url: str :type method: str
:param method: Request method, must be GET or POST :param params: URL parameters, defaults to None
:type method: str :type params: Optional[dict], optional
:param params: URL parameters, defaults to None :param data: POST request data, if the method is GET,
:type params: Optional[dict], optional this dict will be combined with params, defaults to None
:param data: POST request data, if the method is GET, :type data: Optional[dict], optional
this dict will be combined with params, defaults to None :param headers: Custom headers, defaults to None
:type data: Optional[dict], optional :type headers: Optional[dict], optional
:param headers: Custom headers, defaults to None :param sendtoken: If the ajax and SEC token
:type headers: Optional[dict], optional should be sent, defaults to False
:param sendtoken: If the ajax and SEC token :type sendtoken: bool, optional
should be sent, defaults to False :return: API response
:type sendtoken: bool, optional :rtype: requests.Response
:return: API response """
:rtype: requests.Response
""" return self.atconn.request_cloudflare(
url=url, method=method,
return self.atconn.request_cloudflare( params=params, data=data,
url=url, method=method, headers=headers,
params=params, data=data, reqcookies={
headers=headers, 'ATERNOS_SERVER': self.servid
reqcookies={ },
'ATERNOS_SERVER': self.servid sendtoken=sendtoken
}, )
sendtoken=sendtoken
) @property
def subdomain(self) -> str:
@property atdomain = self.domain
def subdomain(self) -> str: return atdomain[:atdomain.find('.')]
atdomain = self.domain
return atdomain[:atdomain.find('.')] @subdomain.setter
def subdomain(self, value: str) -> None:
@subdomain.setter self.atserver_request(
def subdomain(self, value:str) -> None: 'https://aternos.org/panel/ajax/options/subdomain.php',
self.atserver_request( 'GET', params={'subdomain': value},
'https://aternos.org/panel/ajax/options/subdomain.php', sendtoken=True
'GET', params={'subdomain': value}, )
sendtoken=True
) @property
def motd(self) -> str:
@property return self._info['motd']
def motd(self) -> str:
return self._info['motd'] @motd.setter
def motd(self, value: str) -> None:
@motd.setter self.atserver_request(
def motd(self, value:str) -> None: 'https://aternos.org/panel/ajax/options/motd.php',
self.atserver_request( 'POST', data={'motd': value},
'https://aternos.org/panel/ajax/options/motd.php', sendtoken=True
'POST', data={'motd': value}, )
sendtoken=True
) @property
def address(self) -> str:
@property return self._info['displayAddress']
def address(self) -> str:
return self._info['displayAddress'] @property
def domain(self) -> str:
@property return self._info['ip']
def domain(self) -> str:
return self._info['ip'] @property
def port(self) -> int:
@property return self._info['port']
def port(self) -> int:
return self._info['port'] @property
def edition(self) -> int:
@property soft_type = self._info['bedrock']
def edition(self) -> int: return int(soft_type)
soft_type = self._info['bedrock']
return int(soft_type) @property
def software(self) -> str:
@property return self._info['software']
def software(self) -> str:
return self._info['software'] @property
def version(self) -> str:
@property return self._info['version']
def version(self) -> str:
return self._info['version'] @property
def status(self) -> str:
@property return self._info['class']
def status(self) -> str:
return self._info['class'] @property
def status_num(self) -> int:
@property return int(self._info['status'])
def status_num(self) -> int:
return int(self._info['status']) @property
def players_list(self) -> List[str]:
@property return self._info['playerlist']
def players_list(self) -> List[str]:
return self._info['playerlist'] @property
def players_count(self) -> int:
@property return int(self._info['players'])
def players_count(self) -> int:
return int(self._info['players']) @property
def slots(self) -> int:
@property return int(self._info['slots'])
def slots(self) -> int:
return int(self._info['slots']) @property
def ram(self) -> int:
@property return int(self._info['ram'])
def ram(self) -> int:
return int(self._info['ram'])

View file

@ -8,215 +8,224 @@ from typing import TYPE_CHECKING
from .atconnect import REQUA from .atconnect import REQUA
if TYPE_CHECKING: if TYPE_CHECKING:
from .atserver import AternosServer from .atserver import AternosServer
class Streams(enum.Enum): class Streams(enum.Enum):
"""WebSocket streams types""" """WebSocket streams types"""
status = (0,None) status = (0, None)
queue = (1,None) queue = (1, None)
console = (2,'console') console = (2, 'console')
ram = (3,'heap') ram = (3, 'heap')
tps = (4,'tick') tps = (4, 'tick')
def __init__(self, num: int, stream: str) -> None:
self.num = num
self.stream = stream
def __init__(self, num:int, stream:str):
self.num = num
self.stream = stream
class AternosWss: class AternosWss:
"""Class for managing websocket connection """Class for managing websocket connection
:param atserv: :class:`python_aternos.atserver.AternosServer` instance :param atserv: :class:`python_aternos.atserver.AternosServer` instance
:type atserv: python_aternos.atserver.AternosServer :type atserv: python_aternos.atserver.AternosServer
:param autoconfirm: Automatically start server status listener :param autoconfirm: Automatically start server status listener
when AternosWss connects to API to confirm server launching, defaults to `False` when AternosWss connects to API to confirm
:type autoconfirm: bool, optional server launching, defaults to `False`
""" :type autoconfirm: bool, optional
"""
def __init__(self, atserv:'AternosServer', autoconfirm:bool=False) -> None: def __init__(self, atserv: 'AternosServer', autoconfirm: bool = False) -> None:
self.atserv = atserv self.atserv = atserv
self.cookies = atserv.atconn.session.cookies self.cookies = atserv.atconn.session.cookies
self.session = self.cookies['ATERNOS_SESSION'] self.session = self.cookies['ATERNOS_SESSION']
self.servid = atserv.servid self.servid = atserv.servid
self.recv = {} self.recv = {}
self.autoconfirm = autoconfirm self.autoconfirm = autoconfirm
self.confirmed = False self.confirmed = False
async def confirm(self) -> None: async def confirm(self) -> None:
"""Simple way to call AternosServer.confirm from this class""" """Simple way to call AternosServer.confirm from this class"""
self.atserv.confirm() self.atserv.confirm()
def wssreceiver(self, stream:Streams, *args:Any) -> Callable[[Callable[[Any],Coroutine[Any,Any,None]]],Any]: def wssreceiver(self, stream: Streams, *args: Any) -> Callable[[Callable[[Any], Coroutine[Any, Any, None]]], Any]:
"""Decorator that marks your function as a stream receiver. """Decorator that marks your function as a stream receiver.
When websocket receives message from the specified stream, When websocket receives message from the specified stream,
it calls all listeners created with this decorator. it calls all listeners created with this decorator.
:param stream: Stream that your function should listen :param stream: Stream that your function should listen
:type stream: python_aternos.atwss.Streams :type stream: python_aternos.atwss.Streams
:param args: Arguments which will be passed to your function :param args: Arguments which will be passed to your function
:type args: tuple, optional :type args: tuple, optional
:return: ... :return: ...
:rtype: Callable[[Callable[[Any],Coroutine[Any,Any,None]]],Any] :rtype: Callable[[Callable[[Any], Coroutine[Any, Any, None]]], Any]
""" """
def decorator(func:Callable[[Any],Coroutine[Any,Any,None]]) -> None: def decorator(func: Callable[[Any], Coroutine[Any, Any, None]]) -> None:
self.recv[stream] = (func, args) self.recv[stream] = (func, args)
return decorator return decorator
async def connect(self) -> None: async def connect(self) -> None:
"""Connect to the websocket server and start all stream listeners""" """Connect to the websocket server and start all stream listeners"""
headers = [ headers = [
('Host', 'aternos.org'), ('Host', 'aternos.org'),
('User-Agent', REQUA), ('User-Agent', REQUA),
( (
'Cookie', 'Cookie',
f'ATERNOS_SESSION={self.session}; ' + \ f'ATERNOS_SESSION={self.session}; '
f'ATERNOS_SERVER={self.servid}' f'ATERNOS_SERVER={self.servid}'
) )
] ]
self.socket = await websockets.connect( self.socket = await websockets.connect(
'wss://aternos.org/hermes/', 'wss://aternos.org/hermes/',
origin='https://aternos.org', origin='https://aternos.org',
extra_headers=headers extra_headers=headers
) )
@self.wssreceiver(Streams.status) @self.wssreceiver(Streams.status)
async def confirmfunc(msg): async def confirmfunc(msg):
"""Automatically confirm Minecraft server launching""" """Automatically confirm Minecraft server launching"""
if not self.autoconfirm: if not self.autoconfirm:
return return
if msg['class'] == 'queueing' \
and msg['queue']['pending'] == 'pending'\
and not self.confirmed:
self.confirm()
@self.wssreceiver(Streams.status) in_queue = (msg['class'] == 'queueing')
async def streamsfunc(msg): pending = (msg['queue']['pending'] == 'pending')
confirmation = in_queue and pending
"""Automatically starts streams. Detailed description: if confirmation and not self.confirmed:
self.confirm()
According to the websocket messages from the web site, @self.wssreceiver(Streams.status)
Aternos can't receive any data from a stream (e.g. console) until async def streamsfunc(msg):
it requests this stream via the special message to the websocket server:
`{"stream":"console","type":"start"}`
on which the server responses with: `{"type":"connected"}`
Also, there are RAM (used heap) and TPS (ticks per second)
streams that must be enabled before trying to get information.
Enabling the stream for listening the server status is not needed,
these data is sent from API by default, so there's None value in
the second item of its stream type tuple (`<Streams.status: (0, None)>`).
https://github.com/DarkCat09/python-aternos/issues/22#issuecomment-1146788496
"""
if msg['status'] == 2: """Automatically starts streams. Detailed description:
# Automatically start streams
for strm in self.recv:
if not isinstance(strm,Streams):
continue
if strm.stream:
logging.debug(f'Enabling {strm.stream} stream')
await self.send({
'stream': strm.stream,
'type': 'start'
})
await self.wssworker() According to the websocket messages from the web site,
Aternos can't receive any data from a stream (e.g. console) until
it requests this stream via the special message
to the websocket server: `{"stream":"console","type":"start"}`
on which the server responses with: `{"type":"connected"}`
Also, there are RAM (used heap) and TPS (ticks per second)
streams that must be enabled before trying to get information.
Enabling the stream for listening the server status is not needed,
these data is sent from API by default, so there's None value in
the second item of its stream type tuple
(`<Streams.status: (0, None)>`).
https://github.com/DarkCat09/python-aternos/issues/22#issuecomment-1146788496
"""
async def close(self) -> None: if msg['status'] == 2:
# Automatically start streams
for strm in self.recv:
"""Closes websocket connection and stops all listeners""" if not isinstance(strm, Streams):
continue
self.keep.cancel() if strm.stream:
self.msgs.cancel() logging.debug(f'Enabling {strm.stream} stream')
await self.socket.close() await self.send({
del self.socket 'stream': strm.stream,
'type': 'start'
})
async def send(self, obj:Union[Dict[str, Any],str]) -> None: await self.wssworker()
"""Sends a message to websocket server async def close(self) -> None:
:param obj: Message, may be a string or a dict """Closes websocket connection and stops all listeners"""
:type obj: Union[Dict[str, Any],str]
"""
if isinstance(obj, dict): self.keep.cancel()
obj = json.dumps(obj) self.msgs.cancel()
await self.socket.close()
del self.socket
await self.socket.send(obj) async def send(self, obj: Union[Dict[str, Any], str]) -> None:
async def wssworker(self) -> None: """Sends a message to websocket server
"""Starts async tasks in background :param obj: Message, may be a string or a dict
for receiving websocket messages :type obj: Union[Dict[str, Any],str]
and sending keepalive ping""" """
self.keep = asyncio.create_task(self.keepalive()) if isinstance(obj, dict):
self.msgs = asyncio.create_task(self.receiver()) obj = json.dumps(obj)
async def keepalive(self) -> None: await self.socket.send(obj)
"""Each 49 seconds sends keepalive ping to websocket server""" async def wssworker(self) -> None:
try: """Starts async tasks in background
while True: for receiving websocket messages
await asyncio.sleep(49) and sending keepalive ping"""
await self.socket.send('{"type":"\u2764"}')
except asyncio.CancelledError: self.keep = asyncio.create_task(self.keepalive())
pass self.msgs = asyncio.create_task(self.receiver())
async def receiver(self) -> None: async def keepalive(self) -> None:
"""Receives messages from websocket servers """Each 49 seconds sends keepalive ping to websocket server"""
and calls user's streams listeners"""
try: try:
while True: while True:
data = await self.socket.recv() await asyncio.sleep(49)
obj = json.loads(data) await self.socket.send('{"type":"\u2764"}')
msgtype = -1
if obj['type'] == 'line': except asyncio.CancelledError:
msgtype = Streams.console pass
msg = obj['data'].strip('\r\n ')
elif obj['type'] == 'heap': async def receiver(self) -> None:
msgtype = Streams.ram
msg = int(obj['data']['usage'])
elif obj['type'] == 'tick': """Receives messages from websocket servers
msgtype = Streams.tps and calls user's streams listeners"""
ticks = 1000 / obj['data']['averageTickTime']
msg = 20 if ticks > 20 else ticks
elif obj['type'] == 'status': try:
msgtype = Streams.status while True:
msg = json.loads(obj['message']) data = await self.socket.recv()
obj = json.loads(data)
msgtype = -1
if msgtype in self.recv: if obj['type'] == 'line':
msgtype = Streams.console
msg = obj['data'].strip('\r\n ')
# function info tuple: elif obj['type'] == 'heap':
# (function, arguments) msgtype = Streams.ram
func = self.recv[msgtype] msg = int(obj['data']['usage'])
# if arguments is not empty elif obj['type'] == 'tick':
if func[1]: msgtype = Streams.tps
# call the function with args ticks = 1000 / obj['data']['averageTickTime']
coro = func[0](msg, func[1]) msg = 20 if ticks > 20 else ticks
else:
coro = func[0](msg)
# run
await asyncio.create_task(coro)
except asyncio.CancelledError: elif obj['type'] == 'status':
pass msgtype = Streams.status
msg = json.loads(obj['message'])
if msgtype in self.recv:
# function info tuple:
# (function, arguments)
func = self.recv[msgtype]
# if arguments is not empty
if func[1]:
# call the function with args
coro = func[0](msg, func[1])
else:
coro = func[0](msg)
# run
await asyncio.create_task(coro)
except asyncio.CancelledError:
pass

View file

@ -1,43 +1,43 @@
import setuptools import setuptools
with open('README.md', 'rt') as readme: with open('README.md', 'rt') as readme:
long_description = readme.read() long_description = readme.read()
setuptools.setup( setuptools.setup(
name='python-aternos', name='python-aternos',
version='1.0.5', version='1.0.5',
author='Chechkenev Andrey (@DarkCat09)', author='Chechkenev Andrey (@DarkCat09)',
author_email='aacd0709@mail.ru', author_email='aacd0709@mail.ru',
description='An unofficial Aternos API', description='An unofficial Aternos API',
long_description=long_description, long_description=long_description,
long_description_content_type='text/markdown', long_description_content_type='text/markdown',
url='https://github.com/DarkCat09/python-aternos', url='https://github.com/DarkCat09/python-aternos',
project_urls={ project_urls={
'Homepage': 'https://codeberg.org/DarkCat09/python-aternos', 'Homepage': 'https://codeberg.org/DarkCat09/python-aternos',
'GitHub': 'https://github.com/DarkCat09/python-aternos', 'GitHub': 'https://github.com/DarkCat09/python-aternos',
'Bug Tracker': 'https://github.com/DarkCat09/python-aternos/issues', 'Bug Tracker': 'https://github.com/DarkCat09/python-aternos/issues',
'Bug Tracker 2': 'https://codeberg.org/DarkCat09/python-aternos/issues' 'Bug Tracker 2': 'https://codeberg.org/DarkCat09/python-aternos/issues'
}, },
classifiers=[ classifiers=[
'Development Status :: 4 - Beta', 'Development Status :: 4 - Beta',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.10',
'License :: OSI Approved :: Apache Software License', 'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
'Operating System :: Microsoft :: Windows', 'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX :: Linux', 'Operating System :: POSIX :: Linux',
'Operating System :: MacOS' 'Operating System :: MacOS'
], ],
install_requires=[ install_requires=[
'lxml>=4.8.0', 'lxml>=4.8.0',
'cloudscraper>=1.2.58', 'cloudscraper>=1.2.58',
'js2py>=0.71', 'js2py>=0.71',
'websockets>=10.1', 'websockets>=10.1',
'regex>=2022.3.15' 'regex>=2022.3.15'
], ],
packages=['python_aternos'], packages=['python_aternos'],
python_requires=">=3.7", python_requires=">=3.7",
) )

View file

@ -3,78 +3,83 @@ import unittest
from python_aternos import atjsparse from python_aternos import atjsparse
CONV_TOKEN_ARROW = '''(() => {window["AJAX_TOKEN"]=("2r" + "KO" + "A1" + "IFdBcHhEM" + "61" + "6cb");})();'''
CONV_TOKEN_FUNC = '(function(){window["AJAX_TOKEN"]=("2r" + "KO" + "A1" + "IFdBcHhEM" + "61" + "6cb");})()'
class TestJs2Py(unittest.TestCase): class TestJs2Py(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.tests = [] self.tests = []
with open('token.txt', 'rt') as f: with open('token.txt', 'rt') as f:
lines = re.split(r'[\r\n]', f.read()) lines = re.split(r'[\r\n]', f.read())
del lines[len(lines)-1] # Remove empty string del lines[-1] # Remove empty string at the end
self.tests = lines self.tests = lines
self.results = [ self.results = [
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2rKOA1IFdBcHhEM616cb', '2rKOA1IFdBcHhEM616cb',
'2iXh5W5uEYq5fWJIazQ6', '2iXh5W5uEYq5fWJIazQ6',
'CuUcmZ27Fb8bVBNw12Vj', 'CuUcmZ27Fb8bVBNw12Vj',
'YPPe8Ph7vzYaZ9PF9oQP', 'YPPe8Ph7vzYaZ9PF9oQP',
'UfLlemvKEE16ltk0hZNM', 'UfLlemvKEE16ltk0hZNM',
'S1Oban9UGRXVIepREw9q', 'S1Oban9UGRXVIepREw9q',
'S1Oban9UGRXVIepREw9q', 'S1Oban9UGRXVIepREw9q',
'KYDDyT1DWOJTZpNtJWhM', 'KYDDyT1DWOJTZpNtJWhM',
'lZPFwRqIGIf8JKk1LG02', 'lZPFwRqIGIf8JKk1LG02',
'KbxzYCJUrFjWzbeZcAmE', 'KbxzYCJUrFjWzbeZcAmE',
'KbxzYCJUrFjWzbeZcAmE' 'KbxzYCJUrFjWzbeZcAmE'
] ]
def test_base64(self) -> None: def test_base64(self) -> None:
encoded = 'QEhlbGxvIFdvcmxkIQ==' encoded = 'QEhlbGxvIFdvcmxkIQ=='
decoded = atjsparse.atob(encoded) decoded = atjsparse.atob(encoded)
self.assertEqual(decoded, '@Hello World!') self.assertEqual(decoded, '@Hello World!')
def test_conv(self) -> None: def test_conv(self) -> None:
token = '(() => {window["AJAX_TOKEN"]=("2r" + "KO" + "A1" + "IFdBcHhEM" + "61" + "6cb");})();' token = CONV_TOKEN_ARROW
f = atjsparse.to_ecma5_function(token) f = atjsparse.to_ecma5_function(token)
self.assertEqual(f, '(function(){window["AJAX_TOKEN"]=("2r" + "KO" + "A1" + "IFdBcHhEM" + "61" + "6cb");})()') self.assertEqual(f, CONV_TOKEN_FUNC)
def test_ecma6parse(self) -> None: def test_ecma6parse(self) -> None:
code = ''' code = '''
window.t0 = window.t0 =
window['document']&& window['document']&&
!window[["p","Ma"].reverse().join('')]|| !window[["p","Ma"].reverse().join('')]||
!window[["ut","meo","i","etT","s"].reverse().join('')];''' !window[["ut","meo","i","etT","s"].reverse().join('')];'''
part1 = '''window.t1 = Boolean(window['document']);''' part1 = '''window.t1 = Boolean(window['document']);'''
part2 = '''window.t2 = Boolean(!window[["p","Ma"].reverse().join('')]);''' part2 = '''window.t2 = Boolean(!window[["p","Ma"].reverse().join('')]);'''
part3 = '''window.t3 = Boolean(!window[["ut","meo","i","etT","s"].reverse().join('')]);''' part3 = '''window.t3 = Boolean(!window[["ut","meo","i","etT","s"].reverse().join('')]);'''
ctx0 = atjsparse.exec(code) ctx0 = atjsparse.exec(code)
ctx1 = atjsparse.exec(part1) ctx1 = atjsparse.exec(part1)
ctx2 = atjsparse.exec(part2) ctx2 = atjsparse.exec(part2)
ctx3 = atjsparse.exec(part3) ctx3 = atjsparse.exec(part3)
self.assertEqual(ctx1.window['t1'], True) self.assertEqual(ctx0.window['t0'], False)
self.assertEqual(ctx2.window['t2'], False) self.assertEqual(ctx1.window['t1'], True)
self.assertEqual(ctx3.window['t3'], False) self.assertEqual(ctx2.window['t2'], False)
self.assertEqual(ctx3.window['t3'], False)
def test_exec(self) -> None: def test_exec(self) -> None:
for i, f in enumerate(self.tests): for i, f in enumerate(self.tests):
ctx = atjsparse.exec(f) ctx = atjsparse.exec(f)
res = ctx.window['AJAX_TOKEN'] res = ctx.window['AJAX_TOKEN']
self.assertEqual(res, self.results[i]) self.assertEqual(res, self.results[i])
def tearDown(self) -> None: def tearDown(self) -> None:
del self.tests del self.tests
del self.results del self.results