initial checkin mid-refactoring of the threadodo bot.
This commit is contained in:
parent
d82db83c5a
commit
1337621516
22 changed files with 2367 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -160,3 +160,4 @@ cython_debug/
|
|||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
*creds.json
|
||||
|
|
1
LICENSE
1
LICENSE
|
@ -230,3 +230,4 @@ The hypothetical commands `show w' and `show c' should show the appropriate part
|
|||
You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# twitter-wiki-bot
|
||||
# wiki-postbot
|
||||
|
||||
Bot to add tweets using an extended wikilink syntax to the wiki
|
||||
|
||||
Starting with twitter, but then will add masto
|
||||
|
||||
Bot to add tweets using an extended wikilink syntax to the wiki!
|
838
poetry.lock
generated
Normal file
838
poetry.lock
generated
Normal file
|
@ -0,0 +1,838 @@
|
|||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.0"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "21.4.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
|
||||
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "1.0.9"
|
||||
description = "Python bindings for the Brotli compression library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "brotlicffi"
|
||||
version = "1.0.9.2"
|
||||
description = "Python CFFI bindings to the Brotli library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
cffi = ">=1.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2022.6.15"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "1.15.0"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
pycparser = "*"
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "2.0.12"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5.0"
|
||||
|
||||
[package.extras]
|
||||
unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.5"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "commonmark"
|
||||
version = "0.9.1"
|
||||
description = "Python parser for the CommonMark Markdown spec"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "cssselect2"
|
||||
version = "0.6.0"
|
||||
description = "CSS selectors for Python ElementTree"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
tinycss2 = "*"
|
||||
webencodings = "*"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
|
||||
|
||||
[[package]]
|
||||
name = "fonttools"
|
||||
version = "4.33.3"
|
||||
description = "Tools to manipulate font files"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
brotli = {version = ">=1.0.1", optional = true, markers = "platform_python_implementation == \"CPython\" and extra == \"woff\""}
|
||||
brotlicffi = {version = ">=0.8.0", optional = true, markers = "platform_python_implementation != \"CPython\" and extra == \"woff\""}
|
||||
zopfli = {version = ">=0.1.4", optional = true, markers = "extra == \"woff\""}
|
||||
|
||||
[package.extras]
|
||||
all = ["fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "zopfli (>=0.1.4)", "lz4 (>=1.7.4.2)", "matplotlib", "sympy", "skia-pathops (>=0.5.0)", "uharfbuzz (>=0.23.0)", "brotlicffi (>=0.8.0)", "scipy", "brotli (>=1.0.1)", "munkres", "unicodedata2 (>=14.0.0)", "xattr"]
|
||||
graphite = ["lz4 (>=1.7.4.2)"]
|
||||
interpolatable = ["scipy", "munkres"]
|
||||
lxml = ["lxml (>=4.0,<5)"]
|
||||
pathops = ["skia-pathops (>=0.5.0)"]
|
||||
plot = ["matplotlib"]
|
||||
repacker = ["uharfbuzz (>=0.23.0)"]
|
||||
symfont = ["sympy"]
|
||||
type1 = ["xattr"]
|
||||
ufo = ["fs (>=2.2.0,<3)"]
|
||||
unicode = ["unicodedata2 (>=14.0.0)"]
|
||||
woff = ["zopfli (>=0.1.4)", "brotlicffi (>=0.8.0)", "brotli (>=1.0.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "html5lib"
|
||||
version = "1.1"
|
||||
description = "HTML parser based on the WHATWG HTML specification"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.dependencies]
|
||||
six = ">=1.9"
|
||||
webencodings = "*"
|
||||
|
||||
[package.extras]
|
||||
all = ["genshi", "chardet (>=2.2)", "lxml"]
|
||||
chardet = ["chardet (>=2.2)"]
|
||||
genshi = ["genshi"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.3"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "oauthlib"
|
||||
version = "3.2.0"
|
||||
description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
rsa = ["cryptography (>=3.0.0)"]
|
||||
signals = ["blinker (>=1.4.0)"]
|
||||
signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.3"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
||||
|
||||
[[package]]
|
||||
name = "parse"
|
||||
version = "1.19.0"
|
||||
description = "parse() is the opposite of format()"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "9.1.1"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinx-rtd-theme (>=1.0)", "sphinxext-opengraph"]
|
||||
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.0.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.11.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.21"
|
||||
description = "C parser in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pydyf"
|
||||
version = "0.2.0"
|
||||
description = "A low-level PDF generator."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
test = ["pytest", "pytest-xdist", "pytest-flake8", "pytest-isort", "pytest-cov", "coverage", "pillow"]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.12.0"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "pypandoc"
|
||||
version = "1.8.1"
|
||||
description = "Thin wrapper for pandoc."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.8"
|
||||
|
||||
[package.extras]
|
||||
diagrams = ["railroad-diagrams", "jinja2"]
|
||||
|
||||
[[package]]
|
||||
name = "pyphen"
|
||||
version = "0.12.0"
|
||||
description = "Pure Python module to hyphenate text"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.1.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
py = ">=1.8.2"
|
||||
tomli = ">=1.0.0"
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.28.0"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7, <4"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = ">=2.0.0,<2.1.0"
|
||||
idna = ">=2.5,<4"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests-oauthlib"
|
||||
version = "1.3.1"
|
||||
description = "OAuthlib authentication support for Requests."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
oauthlib = ">=3.0.0"
|
||||
requests = ">=2.0.0"
|
||||
|
||||
[package.extras]
|
||||
rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "12.4.4"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.3,<4.0.0"
|
||||
|
||||
[package.dependencies]
|
||||
commonmark = ">=0.9.0,<0.10.0"
|
||||
pygments = ">=2.6.0,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "tinycss2"
|
||||
version = "1.1.1"
|
||||
description = "A tiny CSS parser"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
webencodings = ">=0.4"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "tweepy"
|
||||
version = "4.10.0"
|
||||
description = "Twitter library for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
oauthlib = ">=3.2.0,<4"
|
||||
requests = ">=2.27.0,<3"
|
||||
requests-oauthlib = ">=1.2.0,<2"
|
||||
|
||||
[package.extras]
|
||||
async = ["aiohttp (>=3.7.3,<4)", "async-lru (>=1.0.3,<2)"]
|
||||
dev = ["coverage (>=4.4.2)", "coveralls (>=2.1.0)", "tox (>=3.21.0)"]
|
||||
socks = ["requests[socks] (>=2.27.0,<3)"]
|
||||
test = ["vcrpy (>=1.10.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.9"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "weasyprint"
|
||||
version = "55.0"
|
||||
description = "The Awesome Document Factory"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
cffi = ">=0.6"
|
||||
cssselect2 = ">=0.1"
|
||||
fonttools = {version = ">=4.0.0", extras = ["woff"]}
|
||||
html5lib = ">=1.1"
|
||||
Pillow = ">=4.0.0"
|
||||
pydyf = ">=0.0.3"
|
||||
Pyphen = ">=0.9.1"
|
||||
tinycss2 = ">=1.0.0"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
|
||||
|
||||
[[package]]
|
||||
name = "webencodings"
|
||||
version = "0.5.1"
|
||||
description = "Character encoding aliases for legacy web content"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.2.1"
|
||||
description = "Zopfli module for python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "423cdca5ac87cd46ad86f1a5ac17ae17330ad736b226edfa90b6723e5b230674"
|
||||
|
||||
[metadata.files]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||
]
|
||||
brotli = [
|
||||
{file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"},
|
||||
{file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"},
|
||||
{file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"},
|
||||
{file = "Brotli-1.0.9-cp27-cp27m-win32.whl", hash = "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa"},
|
||||
{file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452"},
|
||||
{file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"},
|
||||
{file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"},
|
||||
{file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"},
|
||||
{file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"},
|
||||
{file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"},
|
||||
{file = "Brotli-1.0.9-cp35-cp35m-win32.whl", hash = "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1"},
|
||||
{file = "Brotli-1.0.9-cp35-cp35m-win_amd64.whl", hash = "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"},
|
||||
{file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"},
|
||||
{file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"},
|
||||
{file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"},
|
||||
{file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"},
|
||||
{file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"},
|
||||
{file = "Brotli-1.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"},
|
||||
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
|
||||
]
|
||||
brotlicffi = [
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:408ec4359f9763280d5c4e0ad29c51d1240b25fdd18719067e972163b4125b98"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2e4629f7690ded66c8818715c6d4dd6a7ff6a4f10fad6186fe99850f781ce210"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:137c4635edcdf593de5ce9d0daa596bf499591b16b8fca5fd72a490deb54b2ee"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:af8a1b7bcfccf9c41a3c8654994d6a81821fdfe4caddcfe5045bfda936546ca3"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9078432af4785f35ab3840587eed7fb131e3fc77eb2a739282b649b343c584dd"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7bb913d5bf3b4ce2ec59872711dc9faaff5f320c3c3827cada2d8a7b793a7753"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:16a0c9392a1059e2e62839fbd037d2e7e03c8ae5da65e9746f582464f7fab1bb"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:94d2810efc5723f1447b332223b197466190518a3eeca93b9f357efb5b22c6dc"},
|
||||
{file = "brotlicffi-1.0.9.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9e70f3e20f317d70912b10dbec48b29114d3dbd0e9d88475cb328e6c086f0546"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:586f0ea3c2eed455d5f2330b9ab4a591514c8de0ee53d445645efcfbf053c69f"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-manylinux1_i686.whl", hash = "sha256:4454c3baedc277fd6e65f983e3eb8e77f4bc15060f69370a0201746e2edeca81"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:52c1c12dad6eb1d44213a0a76acf5f18f64653bd801300bef5e2f983405bdde5"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:21cd400d24b344c218d8e32b394849e31b7c15784667575dbda9f65c46a64b0a"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:71061f8bc86335b652e442260c4367b782a92c6e295cf5a10eff84c7d19d8cf5"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:15e0db52c56056be6310fc116b3d7c6f34185594e261f23790b2fb6489998363"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-win32.whl", hash = "sha256:551305703d12a2dd1ae43d3dde35dee20b1cb49b5796279d4d34e2c6aec6be4d"},
|
||||
{file = "brotlicffi-1.0.9.2-cp35-abi3-win_amd64.whl", hash = "sha256:2be4fb8a7cb482f226af686cd06d2a2cab164ccdf99e460f8e3a5ec9a5337da2"},
|
||||
{file = "brotlicffi-1.0.9.2-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:8e7221d8a084d32d15c7b58e0ce0573972375c5038423dbe83f217cfe512e680"},
|
||||
{file = "brotlicffi-1.0.9.2-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:75a46bc5ed2753e1648cc211dcb2c1ac66116038766822dc104023f67ff4dfd8"},
|
||||
{file = "brotlicffi-1.0.9.2-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1e27c43ef72a278f9739b12b2df80ee72048cd4cbe498f8bbe08aaaa67a5d5c8"},
|
||||
{file = "brotlicffi-1.0.9.2-pp27-pypy_73-win32.whl", hash = "sha256:feb942814285bdc5e97efc77a04e48283c17dfab9ea082d79c0a7b9e53ef1eab"},
|
||||
{file = "brotlicffi-1.0.9.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a6208d82c3172eeeb3be83ed4efd5831552c7cd47576468e50fcf0fb23fcf97f"},
|
||||
{file = "brotlicffi-1.0.9.2-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:408c810c599786fb806556ff17e844a903884e6370ca400bcec7fa286149f39c"},
|
||||
{file = "brotlicffi-1.0.9.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a73099858ee343e8801710a08be8d194f47715ff21e98d92a19ac461058f52d1"},
|
||||
{file = "brotlicffi-1.0.9.2-pp36-pypy36_pp73-win32.whl", hash = "sha256:916b790f967a18a595e61f218c252f83718ac91f24157d622cf0fa710cd26ab7"},
|
||||
{file = "brotlicffi-1.0.9.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba4a00263af40e875ec3d6c7f623cbf8c795b55705da18c64ec36b6bf0848bc5"},
|
||||
{file = "brotlicffi-1.0.9.2-pp37-pypy37_pp73-manylinux1_x86_64.whl", hash = "sha256:df78aa47741122b0d5463f1208b7bb18bc9706dee5152d9f56e0ead4865015cd"},
|
||||
{file = "brotlicffi-1.0.9.2-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:9030cd5099252d16bfa4e22659c84a89c102e94f8e81d30764788b72e2d7cfb7"},
|
||||
{file = "brotlicffi-1.0.9.2-pp37-pypy37_pp73-win32.whl", hash = "sha256:7e72978f4090a161885b114f87b784f538dcb77dafc6602592c1cf39ae8d243d"},
|
||||
{file = "brotlicffi-1.0.9.2.tar.gz", hash = "sha256:0c248a68129d8fc6a217767406c731e498c3e19a7be05ea0a90c3c86637b7d96"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"},
|
||||
{file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"},
|
||||
]
|
||||
cffi = [
|
||||
{file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
|
||||
{file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
|
||||
{file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
|
||||
{file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
|
||||
]
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
|
||||
{file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
||||
]
|
||||
commonmark = [
|
||||
{file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"},
|
||||
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
|
||||
]
|
||||
cssselect2 = [
|
||||
{file = "cssselect2-0.6.0-py3-none-any.whl", hash = "sha256:3a83b2a68370c69c9cd3fcb88bbfaebe9d22edeef2c22d1ff3e1ed9c7fa45ed8"},
|
||||
{file = "cssselect2-0.6.0.tar.gz", hash = "sha256:5b5d6dea81a5eb0c9ca39f116c8578dd413778060c94c1f51196371618909325"},
|
||||
]
|
||||
fonttools = [
|
||||
{file = "fonttools-4.33.3-py3-none-any.whl", hash = "sha256:f829c579a8678fa939a1d9e9894d01941db869de44390adb49ce67055a06cc2a"},
|
||||
{file = "fonttools-4.33.3.zip", hash = "sha256:c0fdcfa8ceebd7c1b2021240bd46ef77aa8e7408cf10434be55df52384865f8e"},
|
||||
]
|
||||
html5lib = [
|
||||
{file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"},
|
||||
{file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
]
|
||||
oauthlib = [
|
||||
{file = "oauthlib-3.2.0-py3-none-any.whl", hash = "sha256:6db33440354787f9b7f3a6dbd4febf5d0f93758354060e802f6c06cb493022fe"},
|
||||
{file = "oauthlib-3.2.0.tar.gz", hash = "sha256:23a8208d75b902797ea29fd31fa80a15ed9dc2c6c16fe73f5d346f83f6fa27a2"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||
]
|
||||
parse = [
|
||||
{file = "parse-1.19.0.tar.gz", hash = "sha256:9ff82852bcb65d139813e2a5197627a94966245c897796760a3a2a8eb66f020b"},
|
||||
]
|
||||
pillow = [
|
||||
{file = "Pillow-9.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:42dfefbef90eb67c10c45a73a9bc1599d4dac920f7dfcbf4ec6b80cb620757fe"},
|
||||
{file = "Pillow-9.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffde4c6fabb52891d81606411cbfaf77756e3b561b566efd270b3ed3791fde4e"},
|
||||
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c857532c719fb30fafabd2371ce9b7031812ff3889d75273827633bca0c4602"},
|
||||
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59789a7d06c742e9d13b883d5e3569188c16acb02eeed2510fd3bfdbc1bd1530"},
|
||||
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d45dbe4b21a9679c3e8b3f7f4f42a45a7d3ddff8a4a16109dff0e1da30a35b2"},
|
||||
{file = "Pillow-9.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e9ed59d1b6ee837f4515b9584f3d26cf0388b742a11ecdae0d9237a94505d03a"},
|
||||
{file = "Pillow-9.1.1-cp310-cp310-win32.whl", hash = "sha256:b3fe2ff1e1715d4475d7e2c3e8dabd7c025f4410f79513b4ff2de3d51ce0fa9c"},
|
||||
{file = "Pillow-9.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5b650dbbc0969a4e226d98a0b440c2f07a850896aed9266b6fedc0f7e7834108"},
|
||||
{file = "Pillow-9.1.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:0b4d5ad2cd3a1f0d1df882d926b37dbb2ab6c823ae21d041b46910c8f8cd844b"},
|
||||
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9370d6744d379f2de5d7fa95cdbd3a4d92f0b0ef29609b4b1687f16bc197063d"},
|
||||
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b761727ed7d593e49671d1827044b942dd2f4caae6e51bab144d4accf8244a84"},
|
||||
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a66fe50386162df2da701b3722781cbe90ce043e7d53c1fd6bd801bca6b48d4"},
|
||||
{file = "Pillow-9.1.1-cp37-cp37m-win32.whl", hash = "sha256:2b291cab8a888658d72b575a03e340509b6b050b62db1f5539dd5cd18fd50578"},
|
||||
{file = "Pillow-9.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1d4331aeb12f6b3791911a6da82de72257a99ad99726ed6b63f481c0184b6fb9"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8844217cdf66eabe39567118f229e275f0727e9195635a15e0e4b9227458daaf"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b6617221ff08fbd3b7a811950b5c3f9367f6e941b86259843eab77c8e3d2b56b"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20d514c989fa28e73a5adbddd7a171afa5824710d0ab06d4e1234195d2a2e546"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088df396b047477dd1bbc7de6e22f58400dae2f21310d9e2ec2933b2ef7dfa4f"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53c27bd452e0f1bc4bfed07ceb235663a1df7c74df08e37fd6b03eb89454946a"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3f6c1716c473ebd1649663bf3b42702d0d53e27af8b64642be0dd3598c761fb1"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-win32.whl", hash = "sha256:c67db410508b9de9c4694c57ed754b65a460e4812126e87f5052ecf23a011a54"},
|
||||
{file = "Pillow-9.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:f054b020c4d7e9786ae0404278ea318768eb123403b18453e28e47cdb7a0a4bf"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:c17770a62a71718a74b7548098a74cd6880be16bcfff5f937f900ead90ca8e92"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3f6a6034140e9e17e9abc175fc7a266a6e63652028e157750bd98e804a8ed9a"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f372d0f08eff1475ef426344efe42493f71f377ec52237bf153c5713de987251"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09e67ef6e430f90caa093528bd758b0616f8165e57ed8d8ce014ae32df6a831d"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66daa16952d5bf0c9d5389c5e9df562922a59bd16d77e2a276e575d32e38afd1"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d78ca526a559fb84faaaf84da2dd4addef5edb109db8b81677c0bb1aad342601"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-win32.whl", hash = "sha256:55e74faf8359ddda43fee01bffbc5bd99d96ea508d8a08c527099e84eb708f45"},
|
||||
{file = "Pillow-9.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c150dbbb4a94ea4825d1e5f2c5501af7141ea95825fadd7829f9b11c97aaf6c"},
|
||||
{file = "Pillow-9.1.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:769a7f131a2f43752455cc72f9f7a093c3ff3856bf976c5fb53a59d0ccc704f6"},
|
||||
{file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:488f3383cf5159907d48d32957ac6f9ea85ccdcc296c14eca1a4e396ecc32098"},
|
||||
{file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b525a356680022b0af53385944026d3486fc8c013638cf9900eb87c866afb4c"},
|
||||
{file = "Pillow-9.1.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6e760cf01259a1c0a50f3c845f9cad1af30577fd8b670339b1659c6d0e7a41dd"},
|
||||
{file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4165205a13b16a29e1ac57efeee6be2dfd5b5408122d59ef2145bc3239fa340"},
|
||||
{file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937a54e5694684f74dcbf6e24cc453bfc5b33940216ddd8f4cd8f0f79167f765"},
|
||||
{file = "Pillow-9.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:baf3be0b9446a4083cc0c5bb9f9c964034be5374b5bc09757be89f5d2fa247b8"},
|
||||
{file = "Pillow-9.1.1.tar.gz", hash = "sha256:7502539939b53d7565f3d11d87c78e7ec900d3c72945d4ee0e2f250d598309a0"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pycparser = [
|
||||
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
||||
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
||||
]
|
||||
pydyf = [
|
||||
{file = "pydyf-0.2.0-py3-none-any.whl", hash = "sha256:f0468bc644d3a5b0a7072d0d92bd5c024cf4beede0df56100d9919a59f15e1f0"},
|
||||
{file = "pydyf-0.2.0.tar.gz", hash = "sha256:06ebc18b4de29fc1450ae49dd142ecd26bd7ba09d0b1919e365fbc3d8af8a622"},
|
||||
]
|
||||
pygments = [
|
||||
{file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
|
||||
{file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
|
||||
]
|
||||
pypandoc = [
|
||||
{file = "pypandoc-1.8.1-py3-none-any.whl", hash = "sha256:3d7eda399f9169f16106362c55a8f12f30ab0575cfd2cdc6e1856b214cc4c38c"},
|
||||
{file = "pypandoc-1.8.1.tar.gz", hash = "sha256:8c1b651d338e8441843b991835f59d561a8473cfe63f0126d330fdb3cb518809"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||
]
|
||||
pyphen = [
|
||||
{file = "pyphen-0.12.0-py3-none-any.whl", hash = "sha256:459020cd320eb200c0c5ba46b98b2278fd34c5546f520fdcd2ce5f8d733eb994"},
|
||||
{file = "pyphen-0.12.0.tar.gz", hash = "sha256:b7d3dfc24b6f2178cdb2b1757ace0bd5d222de3e62c28d22ac578c5f22a13e9b"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
|
||||
{file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"},
|
||||
{file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"},
|
||||
]
|
||||
requests-oauthlib = [
|
||||
{file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"},
|
||||
{file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"},
|
||||
]
|
||||
rich = [
|
||||
{file = "rich-12.4.4-py3-none-any.whl", hash = "sha256:d2bbd99c320a2532ac71ff6a3164867884357da3e3301f0240090c5d2fdac7ec"},
|
||||
{file = "rich-12.4.4.tar.gz", hash = "sha256:4c586de507202505346f3e32d1363eb9ed6932f0c2f63184dea88983ff4971e2"},
|
||||
]
|
||||
six = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
tinycss2 = [
|
||||
{file = "tinycss2-1.1.1-py3-none-any.whl", hash = "sha256:fe794ceaadfe3cf3e686b22155d0da5780dd0e273471a51846d0a02bc204fec8"},
|
||||
{file = "tinycss2-1.1.1.tar.gz", hash = "sha256:b2e44dd8883c360c35dd0d1b5aad0b610e5156c2cb3b33434634e539ead9d8bf"},
|
||||
]
|
||||
tomli = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
tweepy = [
|
||||
{file = "tweepy-4.10.0-py3-none-any.whl", hash = "sha256:f0abbd234a588e572f880f99a094ac321217ff3eade6c0eca118ed6db8e2cf0a"},
|
||||
{file = "tweepy-4.10.0.tar.gz", hash = "sha256:7f92574920c2f233663fff154745fc2bb0d10aedc23617379a912d8e4fefa399"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"},
|
||||
{file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
|
||||
]
|
||||
weasyprint = [
|
||||
{file = "weasyprint-55.0-py3-none-any.whl", hash = "sha256:6a5008b3e1152498f206b2d0791b3e161f507607e31ca42b307cb31b49795462"},
|
||||
{file = "weasyprint-55.0.tar.gz", hash = "sha256:ea5d5f2f159262e38b6e85939d8510e9735a47751a9647c9eaa93c22ced86230"},
|
||||
]
|
||||
webencodings = [
|
||||
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
|
||||
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
|
||||
]
|
||||
zopfli = [
|
||||
{file = "zopfli-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f61ecd57bc47684c44a60e8cecb8e67f633cf238f30cc255627e172119ad72d"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8ca5b541544a7b959fdf5a8f614c52a31002e4be489663d835aadeef3473cb0"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bbcdbfe93dad34f0e30f166092ffdf95e564e415b29732a6f6a52def7bb1c4d3"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c829c577f976b05e4ec583da4f48f31448db97b9f7b65c438d45ba7893aa2a7"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28a103434694ce35cbe4f2380e18077052a78f1a0de061b3c8f1bd35b54c2822"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8a40d9d113902aea0de370dce115051cf9cae4767b50af4fdde66d931b9bcae5"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d8457452a9151b56f17bbb9af57a4764fb41958ab84bf808e3296aefb6e61bca"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b882f85b13c47eb19e7f07bccac7564701424ed5ec6d7ba8886ad6de04110e21"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-win32.whl", hash = "sha256:e9091778e9e0dbbded72c389eace553153102acc9da560870d9d0845c8547a8d"},
|
||||
{file = "zopfli-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:7e2b47662632809d7035f4fc16edbe141c4538158ad74eb3c47532b8bedb8277"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ba4001a8c798a9bb2a59bb30284acd604e0c702477dce69b7fde35a50e55a95b"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fa38b9bb6636fd11b3348dfd6aee4839e71145c3aebc76de4ba44886ec9fd6e"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:58b2bd497273e1344098370d959f837ec1d18bae9bfafad8f4e4a2802cbbf049"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:51537999c2114a68b1c0fac8f7ab2a05e4251d778b568213f1666f04feb79e1c"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:38b1928a5fc5c706ec90aa833ffb5f4512bf886fe41c1b30a95edf7fb09544d6"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7b189d80aeedb986d226e966df9e13fe8ccc28352d9d5c6c1bd3ac208aa79769"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f04a2cf50797dfa6fa954dd24de533f471542aa1923b89381a046a936bcbdcc5"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:3663d25419476b9e999e12ab8f6f5f8d39079bf536947da2cb5f3e45491db6f4"},
|
||||
{file = "zopfli-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:fe9c9276edafb8746c01be15e5d702dcdab41062e2f7fb8af1dd8fa51a18b717"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:338c7b2bba06ff60a73f724b7e1c8a16a5aebe9155edf58cf69b4ff7cb0b46f6"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbf30730f169bbe77b2b6e000bf8b486559e8a36e6ab82f2471d75be4661e6bc"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d067f39d72f364ae94a118df3b4cf2db8a9f53625f497ad0f04bf91ea8720db0"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0190633ca26568f6fa810af9b7c108279d5f565a8aca1224333ff732f565086d"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:faf26674e52a957b8fd76e955b6d89215265f4b8c7e13abf834c84a8e23def9d"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:683c2d521553aa8ca4526c911d419d37223db298f76048ce920aded18ae060cb"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0f45111b31f32aef070be180433fec5de548dc87caf92c5c304d1e3642b12815"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b25540fee3d54e0fb627f911f7e1bed79611ea09aa048708777a34f6f1ac9b70"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-win32.whl", hash = "sha256:cfffa5ad327585754f811fd49518d7170d200ba7888c49ae8629ae94c6c4a77d"},
|
||||
{file = "zopfli-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:95a7ca4571797375b1fa924858cda344767621ef70a43b1b2c0b116e275f42b2"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5ffcdc5ee695da73990f7806f8f1dd9a6d99d338bbe54d08fb3aaa55c9603e27"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2442b99067229d786aa9f9b86976db1ba9602c1b204f361d2901d1bbcaca3441"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:890a83502bb5dff27b1e2b829f8b879d9b7c1383d20a68dc13f0de71da4ff604"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ee224ec851b4b53042ff6ad6e81712c8c9aa18396396acb024fe5a65c6bb8f1"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a9def54c90edd112f785b07a812005ab374e7d0aaf50ecac900ca0c51adab3e7"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9e123bfb16cf86ef5abda375012a97c7d00d989b3519d785ca298326f671a81b"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c5d722b2bfde6dbfbda548e7f6b5b50b57ee06f334b055be24d9bbcdbee60e84"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:29357c34b8de35c05dd04d8706393bfa09b2dd8bb7a331cc5e98e2b8d39d2efd"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-win32.whl", hash = "sha256:fd917247bb0489c924d74186075cf0a2d7d06b3b1413197f9f6ee8fcbd0263a0"},
|
||||
{file = "zopfli-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:2cd55bf9952884f3c0a3a32f95e5ba5fb0e1f371c9b1a99ed41351c1e00f4700"},
|
||||
{file = "zopfli-0.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0f052a07a6fb6b7bf87ed30a099187597eb594a8a573a4426c16a6a852a05d86"},
|
||||
{file = "zopfli-0.2.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c993919cd01ea5c4b0134908bbd59321d9bdbb9085aa30c538fa1289745fe52f"},
|
||||
{file = "zopfli-0.2.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:37071a3dd5ec0c7f27a1f440d6f50ed8171665e7e3b6ae9a6228e6c48d21570c"},
|
||||
{file = "zopfli-0.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:c9b9f73d99080ca2d79600683b8be3395d46a386b4e1d351c6ed6dc261d267e9"},
|
||||
{file = "zopfli-0.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:195209711463a399f0c252b34042d8027f3b41dcb646fc112551cfaef507eab4"},
|
||||
{file = "zopfli-0.2.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8bc92b4008854afa465d647be5ae51b01788ec47cfd10a362dcc865ff898c473"},
|
||||
{file = "zopfli-0.2.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aa18771d1d76c09d2ec2c859b6c55fdad29d2511549b4225437194aca102ad3c"},
|
||||
{file = "zopfli-0.2.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:aa11904718fd27b2ccadf0ac88c1ca0b96ba67ad0c3c2bd584f85de060d04534"},
|
||||
{file = "zopfli-0.2.1.zip", hash = "sha256:e5263d2806e2c1ccb23f52b2972a235d31d42f22f3fa3032cc9aded51e9bf2c6"},
|
||||
]
|
25
pyproject.toml
Normal file
25
pyproject.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[tool.poetry]
|
||||
name = "wiki-postbot"
|
||||
version = "0.1.0"
|
||||
description = "Add posts to the wiki!"
|
||||
authors = ["sneakers-the-rat <JLSaunders987@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
packages = [
|
||||
{ include="wiki_postbot" }
|
||||
]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
wikipostbot = "wiki_postbot.main:main"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
tweepy = "^4.10.0"
|
||||
rich = "^12.4.4"
|
||||
parse = "^1.19.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^7.1.2"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
29
tests/test_commands.py
Normal file
29
tests/test_commands.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from wiki_postbot.bot import WikiPostBot
|
||||
from wiki_postbot.actions import commands
|
||||
import pytest
|
||||
from tweepy import Tweet, StreamResponse
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
['test_str', 'test_res'],
|
||||
[
|
||||
(
|
||||
'@threadodo_bot identify\nname: Myname T. Identifier\norcid: 101001010101010',
|
||||
{'username': 'threadodo_bot', 'command': 'identify',
|
||||
'args': {
|
||||
'name': 'Myname T. Identifier',
|
||||
'orcid': '101001010101010'
|
||||
}}
|
||||
)
|
||||
] )
|
||||
|
||||
def test_parse_identity(test_str, test_res):
|
||||
bot = WikiPostBot()
|
||||
response = StreamResponse(Tweet({'text':test_str, 'id':'1095'}), {}, {}, {})
|
||||
id = commands.Identify(bot)
|
||||
|
||||
assert id.check(response)
|
||||
|
||||
params = id.parse(response)
|
||||
|
||||
assert params.__dict__ == test_res
|
1
wiki_postbot/__init__.py
Normal file
1
wiki_postbot/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from wiki_postbot.bot import WikiPostBot
|
2
wiki_postbot/actions/__init__.py
Normal file
2
wiki_postbot/actions/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
from wiki_postbot.actions.action import Action
|
||||
from wiki_postbot.actions.checks import Mentioned
|
61
wiki_postbot/actions/action.py
Normal file
61
wiki_postbot/actions/action.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
import typing
|
||||
from typing import Optional, Union, Tuple, List, Dict
|
||||
from dataclasses import dataclass
|
||||
|
||||
from tweepy import Response
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from wiki_postbot.bot import WikiPostBot
|
||||
|
||||
|
||||
@dataclass
|
||||
class Result:
|
||||
ok:bool
|
||||
"""
|
||||
Whether the action completed successfully
|
||||
"""
|
||||
log: Optional[str] = None
|
||||
"""
|
||||
Message to log
|
||||
"""
|
||||
reply: Optional[str] = None
|
||||
"""
|
||||
Message to reply with
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class Action(ABC):
|
||||
|
||||
def __init__(self, bot: 'WikiPostBot'):
|
||||
super(Action, self).__init__()
|
||||
self.bot = bot
|
||||
|
||||
#@abstractmethod
|
||||
def do(self, response:Response) -> Result:
|
||||
"""
|
||||
Encapsulate the other actions and uh do the action!
|
||||
Returns:
|
||||
:
|
||||
"""
|
||||
|
||||
#@abstractmethod
|
||||
def check(self, response:Response) -> bool:
|
||||
"""
|
||||
Check if the condition of this action is met
|
||||
"""
|
||||
|
||||
|
||||
#@abstractmethod
|
||||
def get(self, response:Response) -> typing.Any:
|
||||
"""
|
||||
If this action sets something, get its value.
|
||||
|
||||
Eg. if this action sets a
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
43
wiki_postbot/actions/checks.py
Normal file
43
wiki_postbot/actions/checks.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from wiki_postbot.actions.action import Action, Result
|
||||
from wiki_postbot.patterns import WIKILINK
|
||||
from tweepy import Response
|
||||
import re
|
||||
|
||||
class Check(Action):
|
||||
"""
|
||||
Base class for actions that check something about the tagged message
|
||||
to see if we should handle it.
|
||||
"""
|
||||
|
||||
|
||||
class Mentioned(Check):
|
||||
"""
|
||||
Check that we have been directly mentioned in the message
|
||||
"""
|
||||
|
||||
def do(self, response:Response) -> Result:
|
||||
mentioned_users = [u['username'] == self.bot.username for u in response.data.entities.get('mentions', [])]
|
||||
result = Result(ok=any(mentioned_users))
|
||||
if not result.ok:
|
||||
result.log = "Mentioned, but not directly mentioned"
|
||||
|
||||
return result
|
||||
|
||||
class Wikilink(Check):
|
||||
"""
|
||||
Check if a post contains a wikilink
|
||||
"""
|
||||
pattern = WIKILINK
|
||||
"""
|
||||
stolen from the agora bot
|
||||
https://github.com/flancian/agora-bridge/blob/9cfe0a41e55bba4f628875ecf0c8fefd3ad509fd/bots/twitter/agora-bot.py#L48
|
||||
"""
|
||||
|
||||
def do(self, response:Response) -> Result:
|
||||
wikilinks = self.pattern.findall(response.data.text)
|
||||
if len(wikilinks)>0:
|
||||
return Result(ok=True, log=f"Found wikilinks: {wikilinks}")
|
||||
else:
|
||||
return Result(ok=False, log="No wikilinks found")
|
||||
|
||||
|
172
wiki_postbot/actions/commands.py
Normal file
172
wiki_postbot/actions/commands.py
Normal file
|
@ -0,0 +1,172 @@
|
|||
import pdb
|
||||
import sys
|
||||
import typing
|
||||
from typing import Optional, Union, Tuple, List, Dict
|
||||
from wiki_postbot.actions.action import Action, Result
|
||||
from wiki_postbot.logger import init_logger
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
|
||||
from tweepy import Response, StreamResponse
|
||||
import parse
|
||||
|
||||
@dataclass
|
||||
class Command_Params:
|
||||
username: str
|
||||
command: str
|
||||
args: Optional[dict]
|
||||
|
||||
class Command(Action):
|
||||
"""
|
||||
An action invoked by tweeting at the bot and telling it to do something
|
||||
"""
|
||||
name:str
|
||||
command_str = r"@(?P<username>\S{1,})\s*(?P<command>\w*)"
|
||||
arg_str = r"^(\w*?):\s*(.*)"
|
||||
|
||||
@dataclass
|
||||
class Arguments:
|
||||
pass
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Command, self).__init__(*args, **kwargs)
|
||||
self.logger = init_logger('wiki_postbot.action.command.'+self.name, self.bot.basedir)
|
||||
self.command_pattern = re.compile(self.command_str)
|
||||
self.arg_pattern = re.compile(self.arg_str, re.MULTILINE)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse(cls, tweet:Union[str, Response]) -> Union[None, Command_Params]:
|
||||
if isinstance(tweet, (Response, StreamResponse)):
|
||||
tweet = tweet.data.text
|
||||
command_pattern = re.compile(cls.command_str)
|
||||
arg_pattern = re.compile(cls.arg_str, re.MULTILINE)
|
||||
|
||||
command_tup = command_pattern.findall(tweet)
|
||||
command_tup = [c for c in command_tup if len(c) == 2 and len(c[0])>0 and len(c[1])>0]
|
||||
|
||||
if len(command_tup) == 0:
|
||||
return None
|
||||
|
||||
else:
|
||||
param_dict = {'username': command_tup[0][0], 'command': command_tup[0][1]}
|
||||
|
||||
args = arg_pattern.findall(tweet)
|
||||
if args is not None:
|
||||
param_dict['args'] = dict(args)
|
||||
|
||||
return Command_Params(**param_dict)
|
||||
|
||||
|
||||
|
||||
def check(self, response:Response) -> bool:
|
||||
"""
|
||||
Check if the response has a first line like::
|
||||
|
||||
@{bot.username}: {Command.name}
|
||||
|
||||
|
||||
Args:
|
||||
response:
|
||||
|
||||
Returns:
|
||||
bool
|
||||
"""
|
||||
# like
|
||||
# "{@
|
||||
|
||||
# split into lines
|
||||
lines = str(response.data.text).split('\n')
|
||||
test_str = lines[0].strip()
|
||||
self.logger.debug(f"Testing string: {test_str}")
|
||||
|
||||
command_tup = self.command_pattern.findall(test_str)
|
||||
command_tup = [c for c in command_tup if len(c) == 2 and len(c[0]) > 0 and len(c[1]) > 0]
|
||||
|
||||
if len(command_tup) == 0:
|
||||
parse_res = None
|
||||
|
||||
else:
|
||||
parse_res = {'username': command_tup[0][0], 'command': command_tup[0][1]}
|
||||
|
||||
self.logger.debug(f"parse result: {parse_res}")
|
||||
if parse_res is None:
|
||||
self.logger.debug('parse match was None')
|
||||
return False
|
||||
|
||||
if parse_res['username'] == self.bot.username and \
|
||||
parse_res['command'] == self.name:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get(self, response:Response) -> Union[Command_Params, None]:
|
||||
"""Get the value of a command
|
||||
|
||||
Response must have the 'user_id' expansion
|
||||
"""
|
||||
matched = self.bot.client.search_recent_tweets(
|
||||
f"from:{response.includes['users'][0]['username']} @{self.bot.username} \"{self.name}\"",
|
||||
user_auth=True)
|
||||
self.logger.debug("matched get request")
|
||||
self.logger.debug(matched)
|
||||
if matched.data is None:
|
||||
return None
|
||||
|
||||
all_params = [self.parse(d.text) for d in matched.data]
|
||||
found_params = [p for p in all_params if len(p.args)>0]
|
||||
if len(found_params)>0:
|
||||
return found_params[0]
|
||||
else:
|
||||
return all_params[0]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def arg_dict(self, tweet:Union[str, Response]) -> dict:
|
||||
"""
|
||||
Get arguments given to this command
|
||||
"""
|
||||
if isinstance(tweet, Response):
|
||||
tweet = tweet.data.text
|
||||
|
||||
return dict(self.arg_pattern.findall(tweet))
|
||||
|
||||
@classmethod
|
||||
def example(cls) -> str:
|
||||
ex_str = f"@<bot> {cls.name}"
|
||||
keys = [f"{key}:" for key in cls.Arguments.__dataclass_fields__.keys()]
|
||||
ex_str = "\n".join([ex_str, *keys])
|
||||
return ex_str
|
||||
|
||||
|
||||
class Identify(Command):
|
||||
name="identify"
|
||||
|
||||
@dataclass
|
||||
class Arguments:
|
||||
name: str
|
||||
affiliation: Optional[str]
|
||||
orcid: Optional[str]
|
||||
|
||||
def do(self, response:Response) -> Result:
|
||||
params = self.parse(response)
|
||||
if len(params.args) == 0:
|
||||
id = self.get(response)
|
||||
|
||||
if id is None or len(id.args)==0:
|
||||
reply = "No Identity Found! Set one by tweeting:\n"+self.example()
|
||||
log = "Help message given"
|
||||
else:
|
||||
reply = "Previous Identity Found: \n" + "\n".join([f"{key}: {val}" for key, val in id.args.items()])
|
||||
log = "previous identity given"
|
||||
else:
|
||||
reply = "Identity registered! This will be used to identify all threads from your username. If you need to update or remove your identity, delete this tweet, your information is not stored anywhere else."
|
||||
log = f"Identity registered"
|
||||
return Result(ok=True, reply=reply, log=log)
|
||||
|
||||
|
||||
|
||||
|
41
wiki_postbot/actions/inline.py
Normal file
41
wiki_postbot/actions/inline.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
"""
|
||||
Actions triggered by inline text content
|
||||
"""
|
||||
from typing import List, Optional
|
||||
from wiki_postbot.actions import Action
|
||||
|
||||
class Inline(Action):
|
||||
"""
|
||||
An action triggered by the content of a tweet
|
||||
"""
|
||||
|
||||
class WikiLink(Inline):
|
||||
"""
|
||||
Detect a wikilink and add it to the wiki!
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(WikiLink, self).__init__(**kwargs)
|
||||
|
||||
self.wikilinks = None # type: Optional[List[str]]
|
||||
|
||||
def check(self, response: Response) -> bool:
|
||||
"""
|
||||
Check if the condition of this action is met
|
||||
"""
|
||||
wikilinks = self.pattern.findall(response.data.text)
|
||||
if len(wikilinks)>0:
|
||||
return Result(ok=True, log=f"Found wikilinks: {wikilinks}")
|
||||
else:
|
||||
return Result(ok=False, log="No wikilinks found")
|
||||
|
||||
|
||||
|
||||
def do(self, response: Response) -> Result:
|
||||
"""
|
||||
"""
|
||||
|
||||
# @abstractmethod
|
||||
|
||||
|
||||
|
161
wiki_postbot/bot.py
Normal file
161
wiki_postbot/bot.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
from pathlib import Path
|
||||
import logging
|
||||
import tweepy
|
||||
import typing
|
||||
import pdb
|
||||
from typing import List, Optional, Type
|
||||
|
||||
from wiki_postbot.creds import Creds, Zenodo_Creds
|
||||
from wiki_postbot.thread import Thread
|
||||
from wiki_postbot.logger import init_logger
|
||||
from wiki_postbot.actions import checks, commands
|
||||
if typing.TYPE_CHECKING:
|
||||
from wiki_postbot.actions import Action
|
||||
|
||||
|
||||
class WikiPostBot(tweepy.StreamingClient):
|
||||
check_classes = [checks.Wikilink] # type: List[Type[checks.Check]]
|
||||
#command_classes = [commands.Identify] # type: List[Type[commands.Command]]
|
||||
command_classes = []
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
creds:Path = Path('twitter_creds.json'),
|
||||
wiki_creds:Path = Path('wiki_creds.json'),
|
||||
username:str='wikibot3k',
|
||||
following:Optional[List[str]]=None,
|
||||
basedir:Path=Path().home()/"wiki_postbot",
|
||||
loglevel="DEBUG",
|
||||
debug:bool = False
|
||||
):
|
||||
|
||||
self._creds = None
|
||||
self._wiki_creds = None
|
||||
self._client = None
|
||||
self.basedir=Path(basedir)
|
||||
|
||||
self.creds_path = Path(creds)
|
||||
self.wiki_creds_path = Path(wiki_creds)
|
||||
self.username = username
|
||||
self.debug = debug
|
||||
self.following = following
|
||||
self.logger = init_logger('wiki_postbot.bot', basedir, loglevel=loglevel)
|
||||
|
||||
super(WikiPostBot, self).__init__(self.creds.bearer_token)
|
||||
|
||||
self.add_rules(self.rule)
|
||||
|
||||
self.checks = [cls(self) for cls in self.check_classes]
|
||||
self.commands = [cls(self) for cls in self.command_classes]
|
||||
|
||||
@property
|
||||
def rule(self) -> tweepy.StreamRule:
|
||||
"""
|
||||
StreamRule for accounts we're following or if we're mentioned
|
||||
"""
|
||||
rule = f"@{self.username}"
|
||||
if self.following is not None:
|
||||
following = [f'from:{user}' for user in self.following]
|
||||
rule = ' OR '.join([*following, rule])
|
||||
return tweepy.StreamRule(rule)
|
||||
|
||||
|
||||
def on_response(self, response:tweepy.Response):
|
||||
"""
|
||||
Check if the tweet has a wikilink in it
|
||||
"""
|
||||
|
||||
if self.debug:
|
||||
pdb.set_trace()
|
||||
|
||||
self.logger.info(f'Mentioned: {response.data.text}')
|
||||
|
||||
|
||||
# Do checks to see if we should do anything!
|
||||
for check in self.checks:
|
||||
res = check.do(response)
|
||||
if not res.ok:
|
||||
if res.log:
|
||||
self.logger.info(res.log)
|
||||
return
|
||||
|
||||
# Determine what action we should do!
|
||||
for command in self.commands:
|
||||
if command.check(response):
|
||||
self.logger.info(f"Given Command {command.name}")
|
||||
res = command.do(response)
|
||||
if res.log:
|
||||
self.logger.debug(res.log)
|
||||
if res.reply:
|
||||
self.reply(response, res.reply)
|
||||
return
|
||||
|
||||
# TODO: move this to a command class
|
||||
# thread = Thread.from_tweet(self.creds, response)
|
||||
# self.logger.info('thread received')
|
||||
# try:
|
||||
# pdf = thread.to_pdf()
|
||||
# self.logger.info('pdf created')
|
||||
# depo = post_pdf(pdf, thread, self.zenodo_creds)
|
||||
# self.logger.info('posted pdf')
|
||||
# self.logger.debug(depo)
|
||||
#
|
||||
# finally:
|
||||
# pdf.unlink()
|
||||
#
|
||||
# self.reply_completed(response, depo)
|
||||
|
||||
|
||||
def reply_completed(self, response: tweepy.Response, deposit:Deposition):
|
||||
|
||||
self.client.create_tweet(text=f"The preprint of your thread is ready: {deposit.doi_url} - {deposit.title}",
|
||||
in_reply_to_tweet_id=response.data.id)
|
||||
self.logger.info('replied')
|
||||
|
||||
def reply(self, response: tweepy.Response, text:str):
|
||||
self.client.create_tweet(text=text,
|
||||
in_reply_to_tweet_id=response.data.id)
|
||||
|
||||
|
||||
|
||||
def run(self, threaded:bool=False):
|
||||
self.logger.debug('starting')
|
||||
self.filter(threaded=threaded,
|
||||
tweet_fields=[
|
||||
"in_reply_to_user_id",
|
||||
"author_id",
|
||||
"created_at",
|
||||
"conversation_id",
|
||||
"entities",
|
||||
"referenced_tweets"
|
||||
],
|
||||
expansions=[
|
||||
'author_id'
|
||||
]
|
||||
)
|
||||
self.logger.debug('stopped')
|
||||
|
||||
@property
|
||||
def client(self) -> tweepy.Client:
|
||||
if self._client is None:
|
||||
self._client = tweepy.Client(
|
||||
consumer_key=self.creds.api_key,
|
||||
consumer_secret=self.creds.api_secret,
|
||||
access_token=self.creds.access_token,
|
||||
access_token_secret=self.creds.access_secret)
|
||||
return self._client
|
||||
|
||||
@property
|
||||
def creds(self) -> Creds:
|
||||
if self._creds is None:
|
||||
self._creds = Creds.from_json(self.creds_path)
|
||||
return self._creds
|
||||
|
||||
@property
|
||||
def wiki_creds(self) -> Zenodo_Creds:
|
||||
if self._wiki_creds is None:
|
||||
self._wiki_creds = Zenodo_Creds.from_json(self.wiki_creds_path)
|
||||
return self._wiki_creds
|
||||
|
||||
|
||||
|
0
wiki_postbot/checks/__init__.py
Normal file
0
wiki_postbot/checks/__init__.py
Normal file
34
wiki_postbot/creds.py
Normal file
34
wiki_postbot/creds.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
No, not the actual creds. and dont go lookin for em in the git history cuz they aint in there neither
|
||||
"""
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
import typing
|
||||
|
||||
@dataclass
|
||||
class Creds:
|
||||
api_key: str
|
||||
api_secret: str
|
||||
bearer_token: str
|
||||
access_token: typing.Optional[str]
|
||||
access_secret: typing.Optional[str]
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, path:Path) -> 'Creds':
|
||||
with open(path, 'r') as jfile:
|
||||
creds = json.load(jfile)
|
||||
return Creds(**creds)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Zenodo_Creds:
|
||||
access_token:str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, path:Path) -> 'Zenodo_Creds':
|
||||
with open(path, 'r') as jfile:
|
||||
creds = json.load(jfile)
|
||||
return Zenodo_Creds(**creds)
|
||||
|
56
wiki_postbot/logger.py
Normal file
56
wiki_postbot/logger.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
import logging
|
||||
from rich.logging import RichHandler
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import typing
|
||||
from typing import Optional, Union, Tuple, List, Dict, Literal
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
def init_logger(
|
||||
name:Optional[str]=None,
|
||||
basedir:Optional[Path]=None,
|
||||
loglevel:str='DEBUG',
|
||||
loglevel_disk:Optional[str]=None
|
||||
):
|
||||
if name is None:
|
||||
name = 'wiki_postbot'
|
||||
else:
|
||||
if not name.startswith('wiki_postbot'):
|
||||
name = '.'.join(['wiki_postbot', name])
|
||||
|
||||
if loglevel_disk is None:
|
||||
loglevel_disk = loglevel
|
||||
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(loglevel)
|
||||
|
||||
|
||||
if basedir is not None:
|
||||
logger.addHandler(_file_handler(basedir, name, loglevel_disk))
|
||||
|
||||
logger.addHandler(_rich_handler())
|
||||
return logger
|
||||
|
||||
|
||||
def _file_handler(basedir:Path, name:str, loglevel:str="DEBUG") -> RotatingFileHandler:
|
||||
filename = Path(basedir) / '.'.join([name, 'log'])
|
||||
basedir.mkdir(parents=True, exist_ok=True)
|
||||
file_handler = RotatingFileHandler(
|
||||
str(filename),
|
||||
mode='a',
|
||||
maxBytes=2 ** 24,
|
||||
backupCount=5
|
||||
)
|
||||
file_formatter = logging.Formatter("[%(asctime)s] %(levelname)s [%(name)s]: %(message)s")
|
||||
file_handler.setLevel(loglevel)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
return file_handler
|
||||
|
||||
def _rich_handler() -> RichHandler:
|
||||
rich_handler = RichHandler(rich_tracebacks=True, markup=True)
|
||||
rich_formatter = logging.Formatter(
|
||||
"[bold green]\[%(name)s][/bold green] %(message)s",
|
||||
datefmt='[%y-%m-%dT%H:%M:%S]'
|
||||
)
|
||||
rich_handler.setFormatter(rich_formatter)
|
||||
return rich_handler
|
26
wiki_postbot/main.py
Normal file
26
wiki_postbot/main.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import argparse
|
||||
from wiki_postbot.bot import WikiPostBot
|
||||
from pathlib import Path
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Run a wiki_postbot bot!")
|
||||
parser.add_argument('--creds', type=Path, help="Location of twitter credentials .json file",
|
||||
default=Path('twitter_creds.json'))
|
||||
parser.add_argument('--wiki_creds', type=Path, help="Path to wiki credentials .json file",
|
||||
default=Path('wiki_creds.json'))
|
||||
parser.add_argument('-u', '--username', type=str, help="Username of our bot",
|
||||
default="wikibot3k")
|
||||
parser.add_argument('-b', '--basedir', help="Base directory to store files",
|
||||
type=Path, default=Path().home() / "wiki_postbot")
|
||||
parser.add_argument('-l', '--loglevel', type=str, help="Loglevel for wiki_postbot bot, DEBUG, INFO, WARNING, or ERROR",
|
||||
default='DEBUG')
|
||||
parser.add_argument('-d', '--debug', action='store_true', help="Debug responses from bot")
|
||||
return parser.parse_args()
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
tt = WikiPostBot(**args.__dict__)
|
||||
tt.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
6
wiki_postbot/patterns.py
Normal file
6
wiki_postbot/patterns.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Regex patterns
|
||||
"""
|
||||
import re
|
||||
|
||||
WIKILINK = re.compile(r'\[\[(.*?)\]\]', re.IGNORECASE)
|
372
wiki_postbot/templates/report.css
Normal file
372
wiki_postbot/templates/report.css
Normal file
|
@ -0,0 +1,372 @@
|
|||
|
||||
/*stolen from https://github.com/CourtBouillon/weasyprint-samples/blob/master/report/report.css*/
|
||||
|
||||
@page {
|
||||
@top-left {
|
||||
background: #fbc847;
|
||||
content: counter(page);
|
||||
height: 1cm;
|
||||
text-align: center;
|
||||
width: 1cm;
|
||||
}
|
||||
@top-center {
|
||||
background: #fbc847;
|
||||
content: '';
|
||||
display: block;
|
||||
height: .05cm;
|
||||
opacity: .5;
|
||||
width: 100%;
|
||||
}
|
||||
@top-right {
|
||||
content: string(heading);
|
||||
font-size: 9pt;
|
||||
height: 1cm;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@page :blank {
|
||||
@top-left { background: none; content: '' }
|
||||
@top-center { content: none }
|
||||
@top-right { content: none }
|
||||
}
|
||||
@page no-chapter {
|
||||
@top-left { background: none; content: none }
|
||||
@top-center { content: none }
|
||||
@top-right { content: none }
|
||||
}
|
||||
@page :first {
|
||||
background: url(report-cover.jpg) no-repeat center;
|
||||
background-size: cover;
|
||||
margin: 0;
|
||||
}
|
||||
@page chapter {
|
||||
background: #fbc847;
|
||||
margin: 0;
|
||||
@top-left { content: none }
|
||||
@top-center { content: none }
|
||||
@top-right { content: none }
|
||||
}
|
||||
|
||||
html {
|
||||
color: #393939;
|
||||
font-family: Fira Sans;
|
||||
font-size: 11pt;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #fbc847;
|
||||
font-size: 38pt;
|
||||
margin: 5cm 2cm 0 2cm;
|
||||
page: no-chapter;
|
||||
width: 100%;
|
||||
}
|
||||
h2, h3, h4 {
|
||||
color: black;
|
||||
font-weight: 400;
|
||||
}
|
||||
h2 {
|
||||
font-size: 28pt;
|
||||
string-set: heading content();
|
||||
}
|
||||
h3 {
|
||||
font-weight: 300;
|
||||
font-size: 15pt;
|
||||
}
|
||||
h4 {
|
||||
font-size: 13pt;
|
||||
}
|
||||
|
||||
#cover {
|
||||
align-content: space-between;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: 297mm;
|
||||
}
|
||||
#cover address {
|
||||
background: #fbc847;
|
||||
flex: 1 50%;
|
||||
margin: 0 -2cm;
|
||||
padding: 1cm 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
#cover address:first-of-type {
|
||||
padding-left: 3cm;
|
||||
}
|
||||
#contents {
|
||||
break-before: right;
|
||||
break-after: left;
|
||||
page: no-chapter;
|
||||
}
|
||||
#contents h2 {
|
||||
font-size: 20pt;
|
||||
font-weight: 400;
|
||||
margin-bottom: 3cm;
|
||||
}
|
||||
#contents h3 {
|
||||
font-weight: 500;
|
||||
margin: 3em 0 1em;
|
||||
}
|
||||
#contents h3::before {
|
||||
background: #fbc847;
|
||||
content: '';
|
||||
display: block;
|
||||
height: .08cm;
|
||||
margin-bottom: .25cm;
|
||||
width: 2cm;
|
||||
}
|
||||
#contents ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
#contents ul li {
|
||||
border-top: .25pt solid #c1c1c1;
|
||||
margin: .25cm 0;
|
||||
padding-top: .25cm;
|
||||
}
|
||||
#contents ul li::before {
|
||||
color: #fbc847;
|
||||
content: '• ';
|
||||
font-size: 40pt;
|
||||
line-height: 16pt;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
#contents ul li a {
|
||||
color: inherit;
|
||||
text-decoration-line: inherit;
|
||||
}
|
||||
#contents ul li a::before {
|
||||
content: target-text(attr(href));
|
||||
}
|
||||
#contents ul li a::after {
|
||||
color: #fbc847;
|
||||
content: target-counter(attr(href), page);
|
||||
float: right;
|
||||
}
|
||||
|
||||
#columns section {
|
||||
columns: 2;
|
||||
column-gap: 1cm;
|
||||
padding-top: 1cm;
|
||||
}
|
||||
#columns section p {
|
||||
text-align: justify;
|
||||
}
|
||||
#columns section p:first-of-type {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#skills h3 {
|
||||
background: #fbc847;
|
||||
margin: 0 -3cm 1cm;
|
||||
padding: 1cm 1cm 1cm 3cm;
|
||||
width: 21cm;
|
||||
}
|
||||
#skills section {
|
||||
padding: .5cm 0;
|
||||
}
|
||||
#skills section#table-content::before {
|
||||
background: url(table-content.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#heading::before {
|
||||
background: url(heading.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#multi-columns::before {
|
||||
background: url(multi-columns.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#internal-links::before {
|
||||
background: url(internal-links.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#style::before {
|
||||
background: url(style.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section h4 {
|
||||
margin: 0;
|
||||
}
|
||||
#skills section p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#offers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
#offers h2, #offers h3 {
|
||||
width: 100%;
|
||||
}
|
||||
#offers section {
|
||||
width: 30%;
|
||||
}
|
||||
#offers section h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#offers section ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
#offers section ul li:not(:last-of-type) {
|
||||
margin: .5cm 0;
|
||||
}
|
||||
#offers section p {
|
||||
background: #fbc847;
|
||||
display: block;
|
||||
font-size: 15pt;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0;
|
||||
padding: .25cm 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#chapter {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 297mm;
|
||||
justify-content: center;
|
||||
page: chapter;
|
||||
}
|
||||
|
||||
#typography section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 1cm 0;
|
||||
}
|
||||
#typography section h4 {
|
||||
border-top: 1pt solid;
|
||||
flex: 1 25%;
|
||||
margin: 0;
|
||||
}
|
||||
#typography section h4 + * {
|
||||
flex: 1 75%;
|
||||
margin: 0;
|
||||
padding-left: .5cm;
|
||||
}
|
||||
#typography section p {
|
||||
text-align: justify;
|
||||
}
|
||||
#typography section ul {
|
||||
line-height: 2;
|
||||
list-style: none;
|
||||
}
|
||||
#typography section#small-caps p {
|
||||
font-variant: small-caps;
|
||||
}
|
||||
#typography section#ligatures dl {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#typography section#ligatures dl dt {
|
||||
font-weight: 400;
|
||||
width: 30%;
|
||||
}
|
||||
#typography section#ligatures dl dd {
|
||||
flex: 1 70%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#typography section#ligatures .none {
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
#typography section#ligatures .common {
|
||||
font-variant-ligatures: common-ligatures;
|
||||
}
|
||||
#typography section#ligatures .discretionary {
|
||||
font-variant-ligatures: discretionary-ligatures;
|
||||
}
|
||||
#typography section#ligatures .contextual {
|
||||
font-variant-ligatures: contextual;
|
||||
}
|
||||
#typography section#numbers dl {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#typography section#numbers dl dt {
|
||||
font-weight: 400;
|
||||
width: 30%;
|
||||
}
|
||||
#typography section#numbers dl dd {
|
||||
flex: 1 70%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#typography section#numbers #fractions {
|
||||
font-variant-numeric: diagonal-fractions;
|
||||
}
|
||||
#typography section#numbers #ordinals {
|
||||
font-variant-numeric: ordinal;
|
||||
}
|
||||
#typography section#numbers #slashed {
|
||||
font-variant-numeric: slashed-zero;
|
||||
}
|
||||
#typography section#numbers #super {
|
||||
font-variant-position: super;
|
||||
}
|
||||
#typography section#numbers #sub {
|
||||
font-variant-position: sub;
|
||||
}
|
||||
#typography section#figures dl {
|
||||
columns: 4;
|
||||
}
|
||||
#typography section#figures dl dt {
|
||||
font-weight: 400;
|
||||
}
|
||||
#typography section#figures dl dd {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#typography section#figures dl dd ul {
|
||||
padding: 0 1em 0 0;
|
||||
}
|
||||
#typography section#figures #oldstyle {
|
||||
font-variant-numeric: oldstyle-nums;
|
||||
}
|
||||
#typography section#figures #tabular {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
#typography section#figures #old-tabular {
|
||||
font-variant-numeric: oldstyle-nums tabular-nums;
|
||||
}
|
390
wiki_postbot/templates/template.html
Normal file
390
wiki_postbot/templates/template.html
Normal file
|
@ -0,0 +1,390 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<style>
|
||||
|
||||
/*stolen from https://github.com/CourtBouillon/weasyprint-samples/blob/master/report/report.css*/
|
||||
|
||||
@page {
|
||||
@top-left {
|
||||
background: #fbc847;
|
||||
content: counter(page);
|
||||
height: 1cm;
|
||||
text-align: center;
|
||||
width: 1cm;
|
||||
}
|
||||
@top-center {
|
||||
background: #fbc847;
|
||||
content: '';
|
||||
display: block;
|
||||
height: .05cm;
|
||||
opacity: .5;
|
||||
width: 100%;
|
||||
}
|
||||
@top-right {
|
||||
content: string(heading);
|
||||
font-size: 9pt;
|
||||
height: 1cm;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@page :blank {
|
||||
@top-left { background: none; content: '' }
|
||||
@top-center { content: none }
|
||||
@top-right { content: none }
|
||||
}
|
||||
@page no-chapter {
|
||||
@top-left { background: none; content: none }
|
||||
@top-center { content: none }
|
||||
@top-right { content: none }
|
||||
}
|
||||
@page chapter {
|
||||
background: #fbc847;
|
||||
margin: 0;
|
||||
@top-left { content: none }
|
||||
@top-center { content: none }
|
||||
@top-right { content: none }
|
||||
}
|
||||
|
||||
html {
|
||||
color: #393939;
|
||||
font-family: Fira Sans;
|
||||
font-size: 11pt;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/*h1 {*/
|
||||
/* color: #fbc847;*/
|
||||
/* font-size: 38pt;*/
|
||||
/* margin: 5cm 2cm 0 2cm;*/
|
||||
/* page: no-chapter;*/
|
||||
/* width: 100%;*/
|
||||
/*}*/
|
||||
h1, h2, h3, h4 {
|
||||
color: black;
|
||||
font-weight: 400;
|
||||
}
|
||||
h1 {
|
||||
font-size: 36pt;
|
||||
}
|
||||
h2 {
|
||||
font-size: 28pt;
|
||||
}
|
||||
h3 {
|
||||
font-weight: 300;
|
||||
font-size: 15pt;
|
||||
}
|
||||
h4 {
|
||||
font-size: 13pt;
|
||||
}
|
||||
|
||||
#cover {
|
||||
align-content: space-between;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: 297mm;
|
||||
}
|
||||
#cover address {
|
||||
background: #fbc847;
|
||||
flex: 1 50%;
|
||||
margin: 0 -2cm;
|
||||
padding: 1cm 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
#cover address:first-of-type {
|
||||
padding-left: 3cm;
|
||||
}
|
||||
#contents {
|
||||
break-before: right;
|
||||
break-after: left;
|
||||
page: no-chapter;
|
||||
}
|
||||
#contents h2 {
|
||||
font-size: 20pt;
|
||||
font-weight: 400;
|
||||
margin-bottom: 3cm;
|
||||
}
|
||||
#contents h3 {
|
||||
font-weight: 500;
|
||||
margin: 3em 0 1em;
|
||||
}
|
||||
#contents h3::before {
|
||||
background: #fbc847;
|
||||
content: '';
|
||||
display: block;
|
||||
height: .08cm;
|
||||
margin-bottom: .25cm;
|
||||
width: 2cm;
|
||||
}
|
||||
#contents ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
#contents ul li {
|
||||
border-top: .25pt solid #c1c1c1;
|
||||
margin: .25cm 0;
|
||||
padding-top: .25cm;
|
||||
}
|
||||
#contents ul li::before {
|
||||
color: #fbc847;
|
||||
content: '• ';
|
||||
font-size: 40pt;
|
||||
line-height: 16pt;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
#contents ul li a {
|
||||
color: inherit;
|
||||
text-decoration-line: inherit;
|
||||
}
|
||||
#contents ul li a::before {
|
||||
content: target-text(attr(href));
|
||||
}
|
||||
#contents ul li a::after {
|
||||
color: #fbc847;
|
||||
content: target-counter(attr(href), page);
|
||||
float: right;
|
||||
}
|
||||
|
||||
#columns section {
|
||||
columns: 2;
|
||||
column-gap: 1cm;
|
||||
padding-top: 1cm;
|
||||
}
|
||||
#columns section p {
|
||||
text-align: justify;
|
||||
}
|
||||
#columns section p:first-of-type {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#skills h3 {
|
||||
background: #fbc847;
|
||||
margin: 0 -3cm 1cm;
|
||||
padding: 1cm 1cm 1cm 3cm;
|
||||
width: 21cm;
|
||||
}
|
||||
#skills section {
|
||||
padding: .5cm 0;
|
||||
}
|
||||
#skills section#table-content::before {
|
||||
background: url(table-content.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#heading::before {
|
||||
background: url(heading.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#multi-columns::before {
|
||||
background: url(multi-columns.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#internal-links::before {
|
||||
background: url(internal-links.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section#style::before {
|
||||
background: url(style.svg) no-repeat center #fbc847;
|
||||
background-size: 50%;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 2cm;
|
||||
margin-right: .5cm;
|
||||
vertical-align: middle;
|
||||
width: 2cm;
|
||||
}
|
||||
#skills section h4 {
|
||||
margin: 0;
|
||||
}
|
||||
#skills section p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#offers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
#offers h2, #offers h3 {
|
||||
width: 100%;
|
||||
}
|
||||
#offers section {
|
||||
width: 30%;
|
||||
}
|
||||
#offers section h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#offers section ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
#offers section ul li:not(:last-of-type) {
|
||||
margin: .5cm 0;
|
||||
}
|
||||
#offers section p {
|
||||
background: #fbc847;
|
||||
display: block;
|
||||
font-size: 15pt;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0;
|
||||
padding: .25cm 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#chapter {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 297mm;
|
||||
justify-content: center;
|
||||
page: chapter;
|
||||
}
|
||||
|
||||
#typography section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 1cm 0;
|
||||
}
|
||||
#typography section h4 {
|
||||
border-top: 1pt solid;
|
||||
flex: 1 25%;
|
||||
margin: 0;
|
||||
}
|
||||
#typography section h4 + * {
|
||||
flex: 1 75%;
|
||||
margin: 0;
|
||||
padding-left: .5cm;
|
||||
}
|
||||
#typography section p {
|
||||
text-align: justify;
|
||||
}
|
||||
#typography section ul {
|
||||
line-height: 2;
|
||||
list-style: none;
|
||||
}
|
||||
#typography section#small-caps p {
|
||||
font-variant: small-caps;
|
||||
}
|
||||
#typography section#ligatures dl {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#typography section#ligatures dl dt {
|
||||
font-weight: 400;
|
||||
width: 30%;
|
||||
}
|
||||
#typography section#ligatures dl dd {
|
||||
flex: 1 70%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#typography section#ligatures .none {
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
#typography section#ligatures .common {
|
||||
font-variant-ligatures: common-ligatures;
|
||||
}
|
||||
#typography section#ligatures .discretionary {
|
||||
font-variant-ligatures: discretionary-ligatures;
|
||||
}
|
||||
#typography section#ligatures .contextual {
|
||||
font-variant-ligatures: contextual;
|
||||
}
|
||||
#typography section#numbers dl {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#typography section#numbers dl dt {
|
||||
font-weight: 400;
|
||||
width: 30%;
|
||||
}
|
||||
#typography section#numbers dl dd {
|
||||
flex: 1 70%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#typography section#numbers #fractions {
|
||||
font-variant-numeric: diagonal-fractions;
|
||||
}
|
||||
#typography section#numbers #ordinals {
|
||||
font-variant-numeric: ordinal;
|
||||
}
|
||||
#typography section#numbers #slashed {
|
||||
font-variant-numeric: slashed-zero;
|
||||
}
|
||||
#typography section#numbers #super {
|
||||
font-variant-position: super;
|
||||
}
|
||||
#typography section#numbers #sub {
|
||||
font-variant-position: sub;
|
||||
}
|
||||
#typography section#figures dl {
|
||||
columns: 4;
|
||||
}
|
||||
#typography section#figures dl dt {
|
||||
font-weight: 400;
|
||||
}
|
||||
#typography section#figures dl dd {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#typography section#figures dl dd ul {
|
||||
padding: 0 1em 0 0;
|
||||
}
|
||||
#typography section#figures #oldstyle {
|
||||
font-variant-numeric: oldstyle-nums;
|
||||
}
|
||||
#typography section#figures #tabular {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
#typography section#figures #old-tabular {
|
||||
font-variant-numeric: oldstyle-nums tabular-nums;
|
||||
}
|
||||
|
||||
</style>
|
||||
<meta name="description" content="Report example">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<article id="columns">
|
||||
|
||||
$body$
|
||||
|
||||
</article>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
103
wiki_postbot/thread.py
Normal file
103
wiki_postbot/thread.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
from wiki_postbot.creds import Creds
|
||||
import tweepy
|
||||
import typing
|
||||
import re
|
||||
from pathlib import Path
|
||||
import pypandoc
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Author:
|
||||
username: str
|
||||
name: str
|
||||
|
||||
|
||||
class Thread:
|
||||
def __init__(self,
|
||||
tweets: typing.List[tweepy.tweet.Tweet],
|
||||
responses: typing.List[tweepy.Response]
|
||||
):
|
||||
self.tweets = tweets
|
||||
self.responses = responses
|
||||
self.pdf = None
|
||||
self.sort()
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
"""
|
||||
Text from all the tweets in a thread, double new lines between each
|
||||
|
||||
also replacing any @'s with markdown links to profiles i guess
|
||||
"""
|
||||
text = "\n\n".join([t.text for t in self.tweets])
|
||||
# make @'s links
|
||||
text = re.sub(r"@([\w\d_]{1,})", r"[@\1](https://twitter.com/\1)", text)
|
||||
return text
|
||||
|
||||
@property
|
||||
def author(self) -> Author:
|
||||
return Author(
|
||||
"@" + self.responses[0].includes['users'][0].username,
|
||||
self.responses[0].includes['users'][0].name,
|
||||
|
||||
)
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
return f"{self.author.name}'s thread - {self.tweets[0].created_at.strftime('%y-%m-%d %H:%M')}"
|
||||
|
||||
def sort(self):
|
||||
"""
|
||||
sort order of tweets
|
||||
"""
|
||||
self.tweets = sorted(self.tweets, key=lambda x: x.created_at)
|
||||
self.responses = sorted(self.responses, key=lambda x: x.data.created_at)
|
||||
|
||||
|
||||
def to_pdf(self, output_file:typing.Optional[Path]=None,
|
||||
) -> Path:
|
||||
if output_file is None:
|
||||
output_file = Path('.') / f"{self.tweets[0].author_id}_{self.tweets[0].created_at.isoformat()}.pdf"
|
||||
|
||||
pypandoc.convert_text(
|
||||
self.text, to="html", format="md",
|
||||
outputfile=str(output_file),
|
||||
extra_args = [
|
||||
'--pdf-engine=weasyprint',
|
||||
f'--data-dir={Path(__file__).parent}',
|
||||
'--template=./template.html',
|
||||
])
|
||||
self.pdf = output_file
|
||||
return output_file
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_tweet(cls,
|
||||
creds:Creds,
|
||||
response:tweepy.Response,
|
||||
limit:int=100) -> 'Thread':
|
||||
"""
|
||||
Starting with the mentioned tweet, get all of the tweets in a thread in order
|
||||
"""
|
||||
tweet = response.data
|
||||
client = tweepy.Client(creds.bearer_token)
|
||||
tweets = [tweet]
|
||||
responses = [response]
|
||||
while tweet.referenced_tweets is not None and len(tweet.referenced_tweets)>0 and len(tweets)<=limit:
|
||||
replied = [t.id for t in tweet.referenced_tweets if t.type=="replied_to"]
|
||||
if len(replied)==0:
|
||||
# top tweet was qt
|
||||
break
|
||||
replied = replied[0]
|
||||
response = client.get_tweet(id=replied, tweet_fields=[
|
||||
"referenced_tweets",
|
||||
"author_id",
|
||||
"created_at"
|
||||
], expansions=[
|
||||
'author_id'
|
||||
])
|
||||
tweet = response.data
|
||||
tweets.append(tweet)
|
||||
responses.append(response)
|
||||
|
||||
return Thread(tweets, responses)
|
Loading…
Reference in a new issue