mirror of
https://github.com/myned/modufur.git
synced 2024-12-23 22:27:27 +00:00
Merge remote-tracking branch 'origin/master' into dev
This commit is contained in:
commit
f5f8687adf
7 changed files with 1186 additions and 904 deletions
226
.gitignore
vendored
226
.gitignore
vendored
|
@ -1,113 +1,113 @@
|
|||
# Custom
|
||||
*.json
|
||||
*.pyo
|
||||
*.pyc
|
||||
*.DS_Store
|
||||
*.pkl
|
||||
*.png
|
||||
*.bat
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
.static_storage/
|
||||
.media/
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
# Custom
|
||||
*.json
|
||||
*.pyo
|
||||
*.pyc
|
||||
*.DS_Store
|
||||
*.pkl
|
||||
*.png
|
||||
*.bat
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
.static_storage/
|
||||
.media/
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
|
12
Pipfile
12
Pipfile
|
@ -4,6 +4,8 @@ url = "https://pypi.python.org/simple"
|
|||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
[packages]
|
||||
|
||||
|
@ -13,7 +15,15 @@ google-api-python-client = "*"
|
|||
pyrasite = "*"
|
||||
"discord.py" = {extras = ["voice"], git = "https://github.com/Rapptz/discord.py", ref = "rewrite"}
|
||||
aiohttp = "*"
|
||||
"hurry.filesize" = "*"
|
||||
gitpython = "*"
|
||||
gmusicapi = "*"
|
||||
python-telegram-bot = "*"
|
||||
requests = "*"
|
||||
requests-oauthlib = "*"
|
||||
yarl = "*"
|
||||
hurry = "*"
|
||||
pynacl = "*"
|
||||
|
||||
|
||||
[dev-packages]
|
||||
|
||||
|
|
452
Pipfile.lock
generated
452
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "968a2d3c16bf72fd0a52baaa3bbdac69c5dc46dd953874af00479bbe854b678c"
|
||||
"sha256": "c8426f63f07b00a7dcc5220e1210acfc6279bb1eccb40d9a884d34e203bf8f85"
|
||||
},
|
||||
"host-environment-markers": {
|
||||
"implementation_name": "cpython",
|
||||
|
@ -9,15 +9,17 @@
|
|||
"os_name": "posix",
|
||||
"platform_machine": "x86_64",
|
||||
"platform_python_implementation": "CPython",
|
||||
"platform_release": "17.2.0",
|
||||
"platform_release": "17.4.0",
|
||||
"platform_system": "Darwin",
|
||||
"platform_version": "Darwin Kernel Version 17.2.0: Fri Sep 29 18:27:05 PDT 2017; root:xnu-4570.20.62~3/RELEASE_X86_64",
|
||||
"platform_version": "Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64",
|
||||
"python_full_version": "3.6.2",
|
||||
"python_version": "3.6",
|
||||
"sys_platform": "darwin"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
"requires": {
|
||||
"python_version": "3.6"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
|
@ -29,30 +31,30 @@
|
|||
"default": {
|
||||
"aiohttp": {
|
||||
"hashes": [
|
||||
"sha256:cb281a30655efe939550b7dfbe8c5d6e84951caf52510528a88ae7fe483109ae",
|
||||
"sha256:7ff6173cc691a44845e10f452d52932f12b45c6d5db85f5702b17d15addda808",
|
||||
"sha256:e5c2c16ae815d883d4072f2d4542effdeae1d9a464b56bd06186d9eeca1981fe",
|
||||
"sha256:bd920a8b5da1e0ed89c1a688d1ed252ba71189ff747747d38803728a9fba17d4",
|
||||
"sha256:34dc28c31786f43dcd5a25e19da1dd8a5289d338f4ed1a1a9e5d1431ad4b79e2",
|
||||
"sha256:8b5087310db23b1976776beedab05c8aa8e490755e9f3473fc1f07aa0cbe3bb9",
|
||||
"sha256:3654f4913a6c02e742051881f94b7b8a9dfe527318e8d7a7dd2f2d8474b0c082",
|
||||
"sha256:9af1260050bb29db245d284c659e710161c45575f8fb365202551c5fd43ed1c1",
|
||||
"sha256:0589a94051aa0573b28eb5d330a0d12cee891d2ed9fd9323c9ddbe322e88fc15",
|
||||
"sha256:b3b047ecd8b899f99d669f5e22aeabcc77e78f3eb4ee985755ec49c53fcccfc2",
|
||||
"sha256:b73d90381c915c8f873da65520af3e8ac8d26e054b9bc849faa802b25623172a",
|
||||
"sha256:e698a369200b6d4e50a493cf1da4a25f5457f61afd5e7fe91b40f9859f632d1f",
|
||||
"sha256:c58ddcb3ef4dde692e98738f6b3d06650d2b2722910e4d18184530ff8d092d95",
|
||||
"sha256:0965fa5e798b7faacd6417eeb5c476aa6a7d730ba2793106e5bae610fdb043cb",
|
||||
"sha256:3979ef9071bd3c909d33cc333bde356f7063555ad8a0383e53f18dc31131b7d8",
|
||||
"sha256:85caf471c5216d615b01443293cc5ead7502053dedd804da32dd35de816a7436",
|
||||
"sha256:63de1a47530b7bf770f1ad26f342ea94babb451ae04cd3119e87cbfea1c970e5",
|
||||
"sha256:04a8c24a376bb547c0c9b6e721d6ced23062d262d24d5e63a3e383c8b49e14ef",
|
||||
"sha256:9e70efab3bfea1493f3099031e3731a6519adad62e6d3f464b429a80a6e2ff6f",
|
||||
"sha256:c7cb8d2be2f3351a28bee9deca1194e1402171c47b52f16708321ef4ce682333",
|
||||
"sha256:ab9e7ecf375387ae3d17ac25492801860f2726f541d73cf1108fa157e1da75a3",
|
||||
"sha256:42373fbdbe8f09233c17e74f53cee877bc7d5b495b4fc14c32a119255e85e736"
|
||||
"sha256:2e8be4c46083ced9d9bc9ff4d77f31bfcd3e7486613f6138c5aa302d33ea54ed",
|
||||
"sha256:4634dd3bbb68d0c7e5e4bca7571369d53c497b3300d9d678f939038e1b1231ee",
|
||||
"sha256:25825c61688fc95e09d6be19e513e925cb4f08aae4d7a7c38a1fa75e0e4c22bd",
|
||||
"sha256:9e6d6f0bca955923b515f8b5631c4c4f43aa152763852284cbefc89bd544069e",
|
||||
"sha256:6eef1d7eff9e6fa1029f7a62504f88b2b0afce89ced5c95d3a4cf1c2faef1231",
|
||||
"sha256:040eecbc37aa5bd007108388fab6c42b2a01b964c4feac26bdffc8fe8af6c110",
|
||||
"sha256:53988a8cf76c3fb74a759e77b1c2f55ab36880d57c6e7d0d59ad28743a2535fe",
|
||||
"sha256:d51673140330c660e68c182e14164ddba47810dca873bbd28662f31d7d8c0185",
|
||||
"sha256:2fe26e836a1803c7414613c376fe29fc4ae0e5145e3813e1db1854cb05c91a3c",
|
||||
"sha256:15ad4d76bddfd98bf9e48263c70f6603e96d823c5a5c0c842646e9871be72c64",
|
||||
"sha256:7910089093296b5c8f683965044f553b0c5c9c2dbf310a219db76c6e793fea55",
|
||||
"sha256:a19b96f77763ddf0249420438ebfc4d9a470daeb26f6614366d913ff520fa29b",
|
||||
"sha256:b53bc7b44b1115af50bd18d9671972603e5a4934e98dd3f4d671104c070e331d",
|
||||
"sha256:4b6fa00885ec778154244b010acecb862d277e6652b87fcd85c0f4735d26451c",
|
||||
"sha256:7aee5c0750584946fde40da70f0b28fe769f85182f1171acef18a35fd8ecd221"
|
||||
],
|
||||
"version": "==2.3.2"
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e",
|
||||
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"
|
||||
],
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"async-timeout": {
|
||||
"hashes": [
|
||||
|
@ -61,6 +63,13 @@
|
|||
],
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450",
|
||||
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
|
||||
],
|
||||
"version": "==17.4.0"
|
||||
},
|
||||
"beautifulsoup4": {
|
||||
"hashes": [
|
||||
"sha256:7015e76bf32f1f574636c4288399a6de66ce08fb7b2457f628a8d70c0fbabb11",
|
||||
|
@ -69,6 +78,45 @@
|
|||
],
|
||||
"version": "==4.6.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296",
|
||||
"sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d"
|
||||
],
|
||||
"version": "==2018.1.18"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:5d0d7023b72794ea847725680e2156d1d01bc698a9007fccce46d03c904fe093",
|
||||
"sha256:86903c0afab4a3390170aca61f753f5adad8ffff947030719ee44dedc5b68403",
|
||||
"sha256:7d35678a54da0d3f1bc30e3a58a232043753d57c691875b5a75e4e062793bc9a",
|
||||
"sha256:824cac33906be5c8e976f0d950924d88ec058989ef9cd2f77f5cd53cec417635",
|
||||
"sha256:6ca52651f6bd4b8647cb7dee15c82619de3e13490f8e0bc0620830a2245b51d1",
|
||||
"sha256:a183959a4b1e01d6172aeed356e2523ec8682596075aa6cf0003fe08da959a49",
|
||||
"sha256:9532c5bc0108bd0fe43c0eb3faa2ef98a2db60fc0d4019f106b88d46803dd663",
|
||||
"sha256:96652215ef328262b5f1d5647632bd342ac6b31dfbc495b21f1ab27cb06d621d",
|
||||
"sha256:6c99d19225e3135f6190a3bfce2a614cae8eaa5dcaf9e0705d4ccb79a3959a3f",
|
||||
"sha256:12cbf4c04c1ad07124bfc9e928c01e282feac9ec7dd72a18042d4fc56456289a",
|
||||
"sha256:69c37089ccf10692361c8d14dbf4138b00b46741ffe9628755054499f06ed548",
|
||||
"sha256:b8d1454ef627098dc76ccfd6211a08065e6f84efe3754d8d112049fec3768e71",
|
||||
"sha256:cd13f347235410c592f6e36395ee1c136a64b66534f10173bfa4df1dc88f47d0",
|
||||
"sha256:0640f12f04f257c4467075a804a4920a5d07ef91e11c525fc65d715c08231c81",
|
||||
"sha256:89a8d05b96bdeca8fdc89c5fa9469a357d30f6c066262e92c0c8d2e4d3c53cae",
|
||||
"sha256:a67c430a9bde73ae85b0c885fcf41b556760e42ea74c16dc70431a349989b448",
|
||||
"sha256:7a831170b621e98f45ed1d5758325be19619a593924127a0a47af9a72a117319",
|
||||
"sha256:796d0379102e6da5215acfcd20e8e69cca9d97309215b4ce088fe175b1c2f586",
|
||||
"sha256:0fe3b3d571543a4065059d1d3d6d39f4ca6da0f2207ad13547094522e32ead46",
|
||||
"sha256:678135090c311780382b1dd3f828f715583ea8a69687ed053c047d3cec6625d6",
|
||||
"sha256:f4992cd7b4c867f453d44c213ee29e8fd484cf81cfece4b6e836d0982b6fa1cf",
|
||||
"sha256:6d191fb20138fe1948727b20e7b96582b7b7e676135eabf72d910e10bf7bfa65",
|
||||
"sha256:ec208ca16e57904dd7f4c7568665f80b1f7eb7e3214be014560c28def219060d",
|
||||
"sha256:b3653644d6411bf4bd64c1f2ca3cb1b093f98c68439ade5cef328609bbfabf8c",
|
||||
"sha256:f4719d0bafc5f0a67b2ec432086d40f653840698d41fa6e9afa679403dea9d78",
|
||||
"sha256:87f837459c3c78d75cb4f5aadf08a7104db15e8c7618a5c732e60f252279c7a6",
|
||||
"sha256:df9083a992b17a28cd4251a3f5c879e0198bb26c9e808c4647e0a18739f1d11d"
|
||||
],
|
||||
"version": "==1.11.4"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691",
|
||||
|
@ -76,6 +124,13 @@
|
|||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
"sha256:94d1d8905f5010d74bbbd86c30471255661a14187c45f8d7f3e5aa8540fdb2e5",
|
||||
"sha256:7d46dd9f3ea1cf5f06ee0e4e1277ae618cf48dfb10ada7c8427cd46c42702a0e"
|
||||
],
|
||||
"version": "==4.2.1"
|
||||
},
|
||||
"discord.py": {
|
||||
"extras": [
|
||||
"voice"
|
||||
|
@ -83,12 +138,36 @@
|
|||
"git": "https://github.com/Rapptz/discord.py",
|
||||
"ref": "rewrite"
|
||||
},
|
||||
"docopt": {
|
||||
"hashes": [
|
||||
"sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
|
||||
],
|
||||
"version": "==0.6.2"
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
"sha256:e39ced1ab767b5936646cedba8bcce582398233d6a627067d4c6a454c90cfedb"
|
||||
],
|
||||
"version": "==0.16.0"
|
||||
},
|
||||
"gmusicapi": {
|
||||
"hashes": [
|
||||
"sha256:fcee09c2f3cf274f514589eabf82f2281fb184526106432079e641a6bcb697f2"
|
||||
],
|
||||
"version": "==11.0.0"
|
||||
},
|
||||
"google-api-python-client": {
|
||||
"hashes": [
|
||||
"sha256:441d638e5fff7d9f97587aa99387efd0ee13f577c9d4d50820dfda4bb80d0e64",
|
||||
"sha256:bb1f27740f6596f8272a2e1033d93d68e27e8ed5d22d6ab957e3f1d3f8ce05f6"
|
||||
"sha256:2cf9ab83fa62e06717363e8855fb027864caeb35a3197cadb7f0de38356881c4",
|
||||
"sha256:95ce394028754ec537e5791e811511fdd5fabe6f1f8879407a8daed71ecb0b4c"
|
||||
],
|
||||
"version": "==1.6.4"
|
||||
"version": "==1.6.5"
|
||||
},
|
||||
"gpsoauth": {
|
||||
"hashes": [
|
||||
"sha256:1c3f45824d45ac3d06b9d9a0c0eccafe1052505d31ac9a698aef8b00fb0dfc37"
|
||||
],
|
||||
"version": "==0.4.1"
|
||||
},
|
||||
"httplib2": {
|
||||
"hashes": [
|
||||
|
@ -96,6 +175,31 @@
|
|||
],
|
||||
"version": "==0.10.3"
|
||||
},
|
||||
"hurry": {
|
||||
"hashes": [
|
||||
"sha256:d94d3bb94573c17b215257f3f77a9279f2dbb2e794c1862bdb180529edeb8d3f"
|
||||
],
|
||||
"version": "==1.0"
|
||||
},
|
||||
"hurry.filesize": {
|
||||
"hashes": [
|
||||
"sha256:f5368329adbef86accd3bc9490522340bb79260455ae89b1a42c10f63801b9a6"
|
||||
],
|
||||
"version": "==0.9"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4",
|
||||
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f"
|
||||
],
|
||||
"version": "==2.6"
|
||||
},
|
||||
"idna-ssl": {
|
||||
"hashes": [
|
||||
"sha256:1227e44039bd31e02adaeafdbba61281596d623d222643fb021f87f2144ea147"
|
||||
],
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:41f59cbdab232f11680d5d4dec9f2e6782fd24d78e37ee833447702e34e675f4",
|
||||
|
@ -129,32 +233,51 @@
|
|||
],
|
||||
"version": "==4.1.1"
|
||||
},
|
||||
"mechanicalsoup": {
|
||||
"hashes": [
|
||||
"sha256:22423efd025c3eedb06f41d3ff1127174a59f40dc560e82dce143956976195bf"
|
||||
],
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"mock": {
|
||||
"hashes": [
|
||||
"sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1",
|
||||
"sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"
|
||||
],
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"multidict": {
|
||||
"hashes": [
|
||||
"sha256:d12dfcff45b5c0eb3d586289cbf928012e75f93f10f4b9d7af903acb07b3c226",
|
||||
"sha256:f7deb65a184cbe757faaf4c6d2b8f203cb1a11dd44603a34d7befc343253d5bf",
|
||||
"sha256:80ad69b8330135b52a6da7a1f0ab6a278025bb72ea08768966785c829f515a6f",
|
||||
"sha256:3261b631cd6d079e6a20def3306b433794f800ea896dce4f1d5e833ad9bf6f26",
|
||||
"sha256:80e2bd17ea98fe77867771e1ee03433a54fa492a8413966a6caa766bdd6d6e62",
|
||||
"sha256:8eb59892040198741944eafca0c34be4da6b58be7fddf6e491a2d9e7e1767548",
|
||||
"sha256:24ec0d645ca70981e1c84e97e48c874b47f4970a173c8c751be9c213a8658e40",
|
||||
"sha256:167558cfb7c43077b3d8602142109f2b52fae0841a7ff97eb0a7443db199f303",
|
||||
"sha256:80abc93d197e24fb7c5aa5a8be3eacbcbc7115a2ff64e46162a4c2b46cd2f75a",
|
||||
"sha256:ff2b92a41df2e4c5cafaefb782469f3e6053ed00026d75a398521096192d0408",
|
||||
"sha256:5cfb63f0ddfa86ce6e80eee64e4fe886f9dfdb6dbbd724997bccb66513b0e29f",
|
||||
"sha256:50dac1151e34974b04419073ee9726f4180c6daf3454fd4af258824a19ad8c1d",
|
||||
"sha256:ebf1ba2af62dbaa37cf3db346830bb43d40d05a5f1f1966888a620f9b795e7c7",
|
||||
"sha256:3f513f3bf933d7cb6f5741f6676d4fac1e96aa634161c071974f9bb86a7bbac9",
|
||||
"sha256:ef6dc6e2d51b6058aa62cdd44dbf02c250c19eee6ff0065babc0ac126b068d43",
|
||||
"sha256:90dc3b8fefc58a865d64957f8f901d724250ba2a40e02f49d0df0103e96f5afa",
|
||||
"sha256:70630854b820d73ae102440123df38c983d77cd4ae444f3930a6bb6bbda87b76",
|
||||
"sha256:faf2b6447521d2075d03fb5e7c5467bac68f67df1c69e034ebca3afb6b3c619d",
|
||||
"sha256:eb7d5d39463137726138fc14c8458131136b8f4b06ba65f1cecd7aa6abe91df1",
|
||||
"sha256:fa04df1503fae7045883c57db47ba0c07de2ebe4a91c3e64d56f20d3d99e5dc8",
|
||||
"sha256:bce16633f3ee88863e4c9bcd7e037a6133c56fd9e7e7c0776bbaeeddcf154ac4",
|
||||
"sha256:f82e61c7408ed0dce1862100db55595481911f159d6ddec0b375d35b6449509b"
|
||||
"sha256:0fd4d255adcbab3341d64a2fff5acce23409e57bb94e626485dea3db70ddc35e",
|
||||
"sha256:93f1af99bbe75c854370460a60823d6726f9af2196818a64346000d02e074ed7",
|
||||
"sha256:65546242d0c481c0daf0ef20c1be81c075fb763c5f4346f18f748b422fc40f32",
|
||||
"sha256:0462372fc74e4c061335118a4a5992b9a618d6c584b028ef03cf3e9b88a960e2",
|
||||
"sha256:63663541d395ffe4d51a3c021467d0a7b46c965b63fa1646cb46e2e2f1f36415",
|
||||
"sha256:84a1cb5320f1494cd444ca3bd09ddba2e0af0cb210f9263bcf17357ab22671a1",
|
||||
"sha256:241c11614f64535e213ea143efa8b7e598793256601fc795e77075bdfa54f5d6",
|
||||
"sha256:ea8a18ea02bf84981ec93faded773a866554666f13955c92139127892c4bb45c",
|
||||
"sha256:b46ec31bb7729eaa678a3bb1c999460902df1e295fcc093b9aa5f2c7e68d5803",
|
||||
"sha256:608f7eef60e6558418d7da6551dd3d07ccc1290ecc85755d781bd8100322ea5b",
|
||||
"sha256:068e91060e3e211441b1a31f5e65de88fc346490e1fae583c35a75a5295c8ef7",
|
||||
"sha256:288e8f94fb6f586e7386c1f22c979ce3ec866ab23371fa8fef1dd526cd4dfde1",
|
||||
"sha256:503ae54582601b0ff647731fee5efcdff5db1f4da0350febb31b628236a5f0b5",
|
||||
"sha256:6d5f6f26f9025756035c473167b39c5a72e4e519a2286c9399d21f6682e4e5bc",
|
||||
"sha256:e13265feabb1fa26f9cd49cbafd9b5de70ad768093ddb092af477c9823f44f0e",
|
||||
"sha256:50de6f3786ba868ffb7d78d4bcacf0928321f9892366b2f4a0426bba644e3f25",
|
||||
"sha256:16c78b10e897a512aa34ab1969982e42246e53077ae903c1b334926e1ea832d1",
|
||||
"sha256:e04b5bf8581718cf84c1c60bda40221d926ceb06f942ebabfc3baf467a1e34be",
|
||||
"sha256:d99819e9e15e1295a31a757360cab65bc96162870f90c29432564bd8e8999aca",
|
||||
"sha256:cd172509bfc9144395204dd2c0eb305ae5e89f8ad1714ffd7d793607c53c3244",
|
||||
"sha256:3508bea4974ee30fabcf7c8852fca7d9d54d496eaa068bee8311e0ac4df4ade3",
|
||||
"sha256:fb4412490324705dcd2172baa8a3ea58ae23c5f982476805cad58ae929fe2a52"
|
||||
],
|
||||
"version": "==3.3.2"
|
||||
"version": "==4.1.0"
|
||||
},
|
||||
"mutagen": {
|
||||
"hashes": [
|
||||
"sha256:b2a2c2ce87863af12ed7896f341419cd051a3c72c3c6733db9e83060dcadee5e"
|
||||
],
|
||||
"version": "==1.40.0"
|
||||
},
|
||||
"oauth2client": {
|
||||
"hashes": [
|
||||
|
@ -163,39 +286,140 @@
|
|||
],
|
||||
"version": "==4.1.2"
|
||||
},
|
||||
"oauthlib": {
|
||||
"hashes": [
|
||||
"sha256:ce57b501e906ff4f614e71c36a3ab9eacbb96d35c24d1970d2539bbc3ec70ce1"
|
||||
],
|
||||
"version": "==2.0.6"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac",
|
||||
"sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1"
|
||||
],
|
||||
"version": "==3.1.1"
|
||||
},
|
||||
"proboscis": {
|
||||
"hashes": [
|
||||
"sha256:b822b243a7c82030fce0de97bdc432345941306d2c24ef227ca561dd019cd238"
|
||||
],
|
||||
"version": "==1.2.6.0"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:11788df3e176f44e0375fe6361342d7258a457b346504ea259a21b77ffc18a90",
|
||||
"sha256:50c24f0d00b7efb3a72ae638ddc118e713cfe8cef40527afe24f7ebcb878e46d",
|
||||
"sha256:41661f9a442eba2f1967f15333ebe9ecc7e7c51bcbaa2972303ad33a4ca0168e",
|
||||
"sha256:06ec363b74bceb7d018f2171e0892f03ab6816530e2b0f77d725a58264551e48",
|
||||
"sha256:b20f861b55efd8206428c13e017cc8e2c34b40b2a714446eb202bbf0ff7597a6",
|
||||
"sha256:c1f9c36004a7ae6f1ce4a23f06070f6b07f57495f251851aa15cc4da16d08378",
|
||||
"sha256:4d2e665410b0a278d2eb2c0a529ca2366bb325eb2ae34e189a826b71fb1b28cd",
|
||||
"sha256:95b78959572de7d7fafa3acb718ed71f482932ddddddbd29ba8319c10639d863"
|
||||
],
|
||||
"version": "==3.5.1"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:2ccb79b01769d99115aa600d7eed99f524bf752bba8f041dc1c184853514655a",
|
||||
"sha256:0f2d585d22050e90c7d293b6451c83db097df77871974d90efd5a30dc12fcde3"
|
||||
],
|
||||
"version": "==1.4.34"
|
||||
},
|
||||
"pyasn1": {
|
||||
"hashes": [
|
||||
"sha256:5eac8d0c1c1282842c9145ae134990a1baf7616eb4cee9129213fad76eba4f54",
|
||||
"sha256:960a603e677897ea29b9a1327b789b7b8a9e187a89e05fed9c3152b3b7c22954",
|
||||
"sha256:4bb562b9f1fc4526028b2aa4dc027dce08f3ade0780abc593a02ce6dbade6e6c",
|
||||
"sha256:8fa8884056bd5b2c92ca1685e6344121b6b43718b44f0c6eb223958003c9d14a",
|
||||
"sha256:16e896433f84575f0636cd9aa8b24659689268a62e00f17235e1fc23c6b00b25",
|
||||
"sha256:bb6f5d5507621e0298794bc3e75b8f963e886cee388a378ab58e5c35ac024276",
|
||||
"sha256:60bf78784b117979f5517c38308f6965fff6c803660fbb16368d94433953b62a",
|
||||
"sha256:4a677c6c9e484977ed6c6a93714ff06ac374220408afeaeef4ef2af652af0f3d",
|
||||
"sha256:b16fb6097d00bbafc114861b16ea41cfe63e32fed1bdc7cd5905a3e02a992fa3",
|
||||
"sha256:8212bde51ec192e30654efe10e636082738ed728e316049f3685d66b8c92941c",
|
||||
"sha256:8d4f0971682203bdfc93740ee7d3fcba0a7f55629451dbe2d32af2335c55b2be",
|
||||
"sha256:187f2a66d617683f8e82d5c00033b7c8a0287e1da88a9d577aebec321cad4965"
|
||||
"sha256:f81c96761fca60d64b1c9b79ec2e40cf9495a745cf570613079ef324aeb9672b",
|
||||
"sha256:7d626683e3d792cccc608da02498aff37ab4f3dafd8905d6bf755d11f9b26b43",
|
||||
"sha256:e85895087905c65b5b594eb91f7522664c85545b147d5f4d4e7b1b07da8dcbdc",
|
||||
"sha256:5a0db897b311d265cde49615cf783f1c78613138605cdd0f907ecfa5b2aba3ee",
|
||||
"sha256:d5cd6ed995dba16fad0c521cfe31cd2d68400b53fcc2bce93326829be73ab6d1",
|
||||
"sha256:a7efe807c4b83a859e2735c692b92ed7b567cfddc4163763412920041d876c2b",
|
||||
"sha256:b5a9ca48055b9a20f6d1b3d68e38692e5431c86a0f99ea602e61294e891fee5b",
|
||||
"sha256:c07d6e587b2f928366b1f67c09bda026a3e6fcc99e80a744dc67f8fca3895626",
|
||||
"sha256:d84c2aea3cf43780e9e6a19f4e4dddee9f6976519020e64e47c57e5c7a8c3dd2",
|
||||
"sha256:758cb50abddc03e4563fd9e7f03db56e3e87b58c0bd01247360326e5c0c7ffa5",
|
||||
"sha256:0d7f6e959fe53f3960a23d73f35e1fce61348b30915b6664309ca756de7c1f89",
|
||||
"sha256:d258b0a71994f7770599835249cece1caef3c70def868c4915e6e5ca49b67d15"
|
||||
],
|
||||
"version": "==0.3.7"
|
||||
"version": "==0.4.2"
|
||||
},
|
||||
"pyasn1-modules": {
|
||||
"hashes": [
|
||||
"sha256:ea8b89f79724c3cf4ca88bc7327964f0750e5219618805dcc85ca0fdae9e5b34",
|
||||
"sha256:16b086729c7af47a67c9e64cea2f763975b602155319b6c63f1f20e5f0179be7",
|
||||
"sha256:7fc70766b8ef5a62eb43767cd7a1c1b3a6aa5095b263a6f2a734987fd90a35d6",
|
||||
"sha256:92caf877c06c033786f0149dd37ea1abfd2c398a007bb40ae6b1f2c96804c1b2",
|
||||
"sha256:018225e6718cfff7e515bd23efe8c0956e5226e3a416ba829e695c607e8ac58f",
|
||||
"sha256:773641c73f6eaac19b5ed7c3e6c3e733dc43b494282ef067325ea6583763f531",
|
||||
"sha256:3350c74c22eb821acfd22ecf4bbb9abfc1de2bd5befb5befd5b1b7ede2d92ace",
|
||||
"sha256:6c457b5037e6a145a43bf3b5b1db622d20a08d4d1ad9a9bdc22dbef7229b250c",
|
||||
"sha256:d37774d5de3887b1cdce7415209e92da49fcd13b99db1c44c179a93a5f87c8b2",
|
||||
"sha256:6d8ad92e399b3140259b2c5249c49e67806a3eee332ed3734da807733925f04e",
|
||||
"sha256:b437be576bdf440fc0e9307a4334303d117a577f2d809ecb9abd715539cb0109",
|
||||
"sha256:1d303eed5aa54cafeca209d16b8c7ea2c6064735fb61f1bee2e0ed63a0816988"
|
||||
"sha256:b1f395cae2d669e0830cb023aa86f9f283b7a9aa32317d7f80d8e78aa2745812",
|
||||
"sha256:854700bbdd01394e2ada9c1bfbd0ed9f5d0c551350dbbd023e88b11d2771ae06",
|
||||
"sha256:598a6004ec26a8ab40a39ea955068cf2a3949ad9c0030da970f2e1ca4c9f1cc9",
|
||||
"sha256:f53fe5bcebdf318f51399b250fe8325ef3a26d927f012cc0c8e0f9e9af7f9deb",
|
||||
"sha256:47fb6757ab78fe966e7c58b2030b546854f78416d653163f0ce9290cf2278e8b",
|
||||
"sha256:041e9fbafac548d095f5b6c3b328b80792f006196e15a232b731a83c93d59493",
|
||||
"sha256:0cea139045c38f84abaa803bcb4b5e8775ea12a42af10019d942f227acc426c3",
|
||||
"sha256:0cdca76a68dcb701fff58c397de0ef9922b472b1cb3ea9695ca19d03f1869787",
|
||||
"sha256:72fd8b0c11191da088147c6e4678ec53e573923ecf60b57eeac9e97433e09fc2",
|
||||
"sha256:c6747146e95d2b14cc2a8399b2b0bde3f93778f8f9ec704690d2b589c376c137",
|
||||
"sha256:0f2e50d20bc670be170966638fa0ae603f0bc9ed6ebe8e97a6d1d4cef30cc889",
|
||||
"sha256:af00ea8f2022b6287dc375b2c70f31ab5af83989fc6fe9eacd4976ce26cd7ccc"
|
||||
],
|
||||
"version": "==0.1.5"
|
||||
"version": "==0.2.1"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226"
|
||||
],
|
||||
"version": "==2.18"
|
||||
},
|
||||
"pycryptodomex": {
|
||||
"hashes": [
|
||||
"sha256:a2ac2eaafd7dc52920c902dae225badfbd9323f0d1166db0a1614fa55e2dc5b6",
|
||||
"sha256:7464c5e2751e9fa9e3b66f3d7054edf4d837498a151d9a3ba156cb6532344455",
|
||||
"sha256:3588078356c6d6f1a5765fe1b254882e1ed7ce5cd8a8a00fdc4cef507e46868d",
|
||||
"sha256:88f6dfb6b3efd084df29d4ed312dde085f4a7cc9efada59998f75a8826e255a2",
|
||||
"sha256:565fa996de2ab58cae9a7b7241c7fd84e6bd705a05ff89e2ea5c4d3e6c755c58",
|
||||
"sha256:b95adb32f32bc85f1c6c1d4d344a73a5fddf7c69b3a6551e499963022200fc5e",
|
||||
"sha256:14bdfc08d38a17fa54027b51c122ce175b84f979781a2e11e86015dd0dfa4157",
|
||||
"sha256:0307cfa4c8aff4dd943933704fc4eae2119f42439d1fe456a594bee44a7d860b",
|
||||
"sha256:2b64915a430fbf992c75751c13cabecf27ba8dd43fdcc8bc0551302d87ef4c91",
|
||||
"sha256:acc287965dd1f08bb5b2e662289111e24fc5e7754c79571a20803fd5ce307c47",
|
||||
"sha256:25d038c3d18b59e07acb0be516396d0c8ed3b098761b0f597c7f03c7516e7699",
|
||||
"sha256:3ebc14df23b8bcc2734aff6255b7047b8c2f28779923f0f36f17c2d2d9fc32dc",
|
||||
"sha256:da75c00a761e456fb99be7e28b60568163c6d3d631f305ffc9f1365c8991c669",
|
||||
"sha256:1a1a4b604dd9dab1a55d43972e853be0c2fd0c50f3589f8c0e18febdb361235c",
|
||||
"sha256:90c9a70005d5c02886ab98ef4e2d577347e3c41399aa0c35a4eac4ee27b1d718",
|
||||
"sha256:51b789c2ccdc30ebf0b1dfcb2095a5e10ebaf68e2cd89bc051b1887a8e8e14d6",
|
||||
"sha256:e27f557e8aab1dbfdd1a51f0053437baf5e13fc6f04c07fbe3a0da7a5c8c51f2",
|
||||
"sha256:626463deb7f760507f98504413fb6035dcff7c8f0b36c6e216605680caa337f0",
|
||||
"sha256:1088099e88ba7238cebd3c44b38edbe7130b229a3d7d7b4c9b0ed06533dd13e6",
|
||||
"sha256:bf2a5b6eee8ca198eea13bcbd2df62721ed9b5f5c8660ac56be7249ff349f582",
|
||||
"sha256:46a3c64f377f14fe9ccaa7b3307de76e312edd0cf7543edb6f159e519081254c",
|
||||
"sha256:bc30c42399b268788407a1ac692ad4011e774809034e7a0bc9559b8feb727ea1",
|
||||
"sha256:02dda17f54cbb37dcad6bd52c21f1208be1e435c7c8b48922366dad989a0c597"
|
||||
],
|
||||
"version": "==3.4.12"
|
||||
},
|
||||
"pynacl": {
|
||||
"hashes": [
|
||||
"sha256:0bfa0d94d2be6874e40f896e0a67e290749151e7de767c5aefbad1121cad7512",
|
||||
"sha256:1d33e775fab3f383167afb20b9927aaf4961b953d76eeb271a5703a6d756b65b",
|
||||
"sha256:eb2acabbd487a46b38540a819ef67e477a674481f84a82a7ba2234b9ba46f752",
|
||||
"sha256:14339dc233e7a9dda80a3800e64e7ff89d0878ba23360eea24f1af1b13772cac",
|
||||
"sha256:cf6877124ae6a0698404e169b3ba534542cfbc43f939d46b927d956daf0a373a",
|
||||
"sha256:eeee629828d0eb4f6d98ac41e9a3a6461d114d1d0aa111a8931c049359298da0",
|
||||
"sha256:d0eb5b2795b7ee2cbcfcadacbe95a13afbda048a262bd369da9904fecb568975",
|
||||
"sha256:11aa4e141b2456ce5cecc19c130e970793fa3a2c2e6fbb8ad65b28f35aa9e6b6",
|
||||
"sha256:8ac1167195b32a8755de06efd5b2d2fe76fc864517dab66aaf65662cc59e1988",
|
||||
"sha256:d795f506bcc9463efb5ebb0f65ed77921dcc9e0a50499dedd89f208445de9ecb",
|
||||
"sha256:be71cd5fce04061e1f3d39597f93619c80cdd3558a6c9ba99a546f144a8d8101",
|
||||
"sha256:2a42b2399d0428619e58dac7734838102d35f6dcdee149e0088823629bf99fbb",
|
||||
"sha256:73a5a96fb5fbf2215beee2353a128d382dbca83f5341f0d3c750877a236569ef",
|
||||
"sha256:d8aaf7e5d6b0e0ef7d6dbf7abeb75085713d0100b4eb1a4e4e857de76d77ac45",
|
||||
"sha256:2dce05ac8b3c37b9e2f65eab56c544885607394753e9613fd159d5e2045c2d98",
|
||||
"sha256:f5ce9e26d25eb0b2d96f3ef0ad70e1d3ae89b5d60255c462252a3e456a48c053",
|
||||
"sha256:6453b0dae593163ffc6db6f9c9c1597d35c650598e2c39c0590d1757207a1ac2",
|
||||
"sha256:fabf73d5d0286f9e078774f3435601d2735c94ce9e514ac4fb945701edead7e4",
|
||||
"sha256:13bdc1fe084ff9ac7653ae5a924cae03bf4bb07c6667c9eb5b6eb3c570220776",
|
||||
"sha256:8f505f42f659012794414fa57c498404e64db78f1d98dfd40e318c569f3c783b",
|
||||
"sha256:04e30e5bdeeb2d5b34107f28cd2f5bbfdc6c616f3be88fc6f53582ff1669eeca",
|
||||
"sha256:8abb4ef79161a5f58848b30ab6fb98d8c466da21fdd65558ce1d7afc02c70b5f",
|
||||
"sha256:e0d38fa0a75f65f556fb912f2c6790d1fa29b7dd27a1d9cc5591b281321eaaa9"
|
||||
],
|
||||
"version": "==1.2.1"
|
||||
},
|
||||
"pyrasite": {
|
||||
"hashes": [
|
||||
|
@ -203,6 +427,34 @@
|
|||
],
|
||||
"version": "==2.0"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c",
|
||||
"sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca"
|
||||
],
|
||||
"version": "==2.6.1"
|
||||
},
|
||||
"python-telegram-bot": {
|
||||
"hashes": [
|
||||
"sha256:ca5e1d257702d194ad5a5a0e6ccffb31203f1f783bf1c5da60c128f083032c44",
|
||||
"sha256:f5c3233bea7c7adf165e31225bbe9f28717e9f1f5070ebe99a4757c31c27ab28"
|
||||
],
|
||||
"version": "==9.0.0"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
|
||||
"sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
|
||||
],
|
||||
"version": "==2.18.4"
|
||||
},
|
||||
"requests-oauthlib": {
|
||||
"hashes": [
|
||||
"sha256:50a8ae2ce8273e384895972b56193c7409601a66d4975774c60c2aed869639ca",
|
||||
"sha256:883ac416757eada6d3d07054ec7092ac21c7f35cb1d2cf82faf205637081f468"
|
||||
],
|
||||
"version": "==0.8.0"
|
||||
},
|
||||
"rsa": {
|
||||
"hashes": [
|
||||
"sha256:43f682fea81c452c98d09fc316aae12de6d30c4b5c84226642cf8f8fd1c93abd",
|
||||
|
@ -225,23 +477,37 @@
|
|||
],
|
||||
"version": "==3.0.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b",
|
||||
"sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
|
||||
],
|
||||
"version": "==1.22"
|
||||
},
|
||||
"validictory": {
|
||||
"hashes": [
|
||||
"sha256:eb7ec9d811f3cf062fb943ce369a9b34a9f291037217ecf5a40a5f2421d29c0a",
|
||||
"sha256:3a87b84658592f75f37d6bab77ac223774c9989dc7349c8aad19a424770835ba"
|
||||
],
|
||||
"version": "==1.1.2"
|
||||
},
|
||||
"yarl": {
|
||||
"hashes": [
|
||||
"sha256:8b6a495e754269ba3817957dfe713532b7d5485cbcf3fce3e8624832f1156e3c",
|
||||
"sha256:14ff708e9813b961d5c0b9a30ae8323591deffca0ef4fa93fbaba94ec44f3560",
|
||||
"sha256:8756000c3ccf91acf9557401892864d2c869c0ae7c62e5d4fc3dd20ed5e8a3a7",
|
||||
"sha256:728b9b26d5927fd35b102c62c476bace5c5e0fb1275e7d4bb169d50720b1d687",
|
||||
"sha256:3a68281e8031b9eae9a1c186eff725ed31a66d4dbfd4dc7c0ceb1a15d4a0b09f",
|
||||
"sha256:dfacdfd8b350a10d66d2af0ef59fa56564cc03bcdcdd93a1aa6e65e9306f5882",
|
||||
"sha256:42d329be7cafa9210c17349fd2db90b967b76fc6fd411f9abaf450a9e7f7e2e4",
|
||||
"sha256:8d4a3ad001747e1225af47d46da935e507e7fe5e315bb4dbd235d5460b722488",
|
||||
"sha256:4dcf2e89e65745c4ae597a724a612dbdb2f38e92c0a5f145c9ec1701d4ddf384",
|
||||
"sha256:44e4ce0f59abbf7745ee450c3c4aa261a4deef83c7e1a3624dcc9db094d630e5",
|
||||
"sha256:626113e8649ff2e205ea49432c4c94f730326852905ba411fb82006e443f3231",
|
||||
"sha256:bfb4256496da1b1475a72e669464db539eca7b0fc3bddd231a64cf8afadb7efb",
|
||||
"sha256:25fe681a982f2cec567df8abac7cbd2ac27016e4aec89193945cab0643bfdb42"
|
||||
"sha256:e938046ce94bcb21e4641712eee3f3c211fc1e5a4601fd89c031e638022097a3",
|
||||
"sha256:42a7fc3d31c4c5b7fb7102380e1518e5af88e3bec7e89acf1213973e880bff6f",
|
||||
"sha256:d861e697e4f1dbd614c29c7594f0dd3dbc48144f1e1f9ed8380604c7dde6cb19",
|
||||
"sha256:e2dbf932bc9519281393973187075f1c2261a9e58032265ae47b2e79ece30ad8",
|
||||
"sha256:f00d067a08836c03646ace00baaf59697e978d2f1958b090c3a098c59221e175",
|
||||
"sha256:0a5d90f6d6ed6204e58b9d10882ef1a1193010e76e744ba39e848e8ddf96ab7a",
|
||||
"sha256:1f96aa307067766c3edfc075739e5da8c790178d98f95e033fc8e22b8f2bc920",
|
||||
"sha256:c9adaa61e1d9487c69f40561a89fc68640dba05e014ed29315b586e106a95671",
|
||||
"sha256:9d25d6d6865979a5216b5bc2b9bd913dbaff636f74411baaa3e79c708ebeafbb",
|
||||
"sha256:c4c7682551c0b10b5c6fe0e359745759bd6298ab5839d9433ad0ed820def0d40",
|
||||
"sha256:2aec0ae0c50997e1a7af3ada4983e11caf212eee73eff6876ce26cf3a5072644",
|
||||
"sha256:f96d8bd39ee85cb9e842e3c7ad8c7cfb875bebfe07d63e87ca3e3984cb2f1546",
|
||||
"sha256:6af895b45bd49254cc309ac0fe6e1595636a024953d710e01114257736184698"
|
||||
],
|
||||
"version": "==0.13.0"
|
||||
"version": "==1.1.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
|
10
README.md
10
README.md
|
@ -1,5 +1,5 @@
|
|||
# Modufur
|
||||
Discord booru bot with a side of management and tasking.
|
||||
|
||||
Credits:
|
||||
Rapptz/discord.py
|
||||
# Modufur
|
||||
Discord booru bot with a side of management and tasking.
|
||||
|
||||
Credits:
|
||||
Rapptz/discord.py
|
||||
|
|
|
@ -1,235 +1,235 @@
|
|||
import asyncio
|
||||
import traceback as tb
|
||||
from contextlib import suppress
|
||||
from datetime import datetime as dt
|
||||
|
||||
import discord as d
|
||||
from discord import errors as err
|
||||
from discord.ext import commands as cmds
|
||||
from discord.ext.commands import errors as errext
|
||||
|
||||
from misc import exceptions as exc
|
||||
from misc import checks
|
||||
from utils import utils as u
|
||||
|
||||
|
||||
class Administration:
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.RATE_LIMIT = u.RATE_LIMIT
|
||||
self.queue = asyncio.Queue()
|
||||
self.deleting = False
|
||||
|
||||
if u.tasks['auto_del']:
|
||||
for channel in u.tasks['auto_del']:
|
||||
temp = self.bot.get_channel(channel)
|
||||
self.bot.loop.create_task(self.queue_for_deletion(temp))
|
||||
print('STARTED : auto-deleting in #{}'.format(temp.name))
|
||||
self.deleting = True
|
||||
self.bot.loop.create_task(self.delete())
|
||||
|
||||
@cmds.group(aliases=['pru', 'purge', 'pur', 'clear', 'cl'], hidden=True)
|
||||
@cmds.is_owner()
|
||||
async def prune(self, ctx):
|
||||
pass
|
||||
|
||||
@prune.group(name='user', aliases=['u', 'member', 'm'])
|
||||
async def _prune_user(self, ctx):
|
||||
pass
|
||||
|
||||
@_prune_user.command(name='channel', aliases=['channels', 'chans', 'chan', 'ch', 'c'])
|
||||
async def _prune_user_channel(self, ctx, user: d.User, *channels: d.TextChannel):
|
||||
def confirm(r, u):
|
||||
if u is ctx.author:
|
||||
if r.emoji == '\N{OCTAGONAL SIGN}':
|
||||
raise exc.Abort
|
||||
if r.emoji == '\N{THUMBS UP SIGN}':
|
||||
return True
|
||||
return False
|
||||
|
||||
if not channels:
|
||||
channels = [ctx.channel]
|
||||
|
||||
try:
|
||||
pruning = await ctx.send(f'\N{HOURGLASS} **Pruning** {user.mention}**\'s messages from** {"**,** ".join([channel.mention for channel in channels])} **might take some time.** Proceed, {ctx.author.mention}?')
|
||||
await pruning.add_reaction('\N{THUMBS UP SIGN}')
|
||||
await pruning.add_reaction('\N{OCTAGONAL SIGN}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await self.bot.wait_for('reaction_add', check=confirm, timeout=10 * 60)
|
||||
|
||||
deleting = await ctx.send(f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages...**')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
c = 0
|
||||
for channel in channels:
|
||||
await deleting.edit(content=f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages from** {channel.mention}')
|
||||
|
||||
deleted = await channel.purge(check=lambda m: m.author.id == user.id, before=pruning, limit=None)
|
||||
c += len(deleted)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
for channel in channels:
|
||||
missed = 0
|
||||
async for message in channel.history(before=pruning, limit=None):
|
||||
if message.author.id == user.id:
|
||||
missed += 1
|
||||
|
||||
if missed > 0:
|
||||
await ctx.send(f'\N{DOUBLE EXCLAMATION MARK} `{missed}` **messages were not deleted in** {channel.mention}')
|
||||
|
||||
await ctx.send(f'\N{WHITE HEAVY CHECK MARK} **Finished deleting** `{c}` **of** {user.mention}**\'s messages**')
|
||||
|
||||
except exc.Abort:
|
||||
await ctx.send('**Deletion aborted**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
except TimeoutError:
|
||||
await ctx.send('**Deletion timed out**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
|
||||
@_prune_user.command(name='all', aliases=['a'], brief='Prune a user\'s messages from the guild', description='about flag centers on message 50 of 101 messages\n\npfg \{user id\} [before|after|about] [\{message id\}]\n\nExample:\npfg \{user id\} before \{message id\}', hidden=True)
|
||||
@cmds.is_owner()
|
||||
async def _prune_user_all(self, ctx, user: d.User):
|
||||
def confirm(r, u):
|
||||
if u is ctx.author:
|
||||
if r.emoji == '\N{OCTAGONAL SIGN}':
|
||||
raise exc.Abort
|
||||
if r.emoji == '\N{THUMBS UP SIGN}':
|
||||
return True
|
||||
return False
|
||||
|
||||
try:
|
||||
pruning = await ctx.send(f'\N{HOURGLASS} **Pruning** {user.mention}**\'s messages might take some time.** Proceed, {ctx.author.mention}?')
|
||||
await pruning.add_reaction('\N{THUMBS UP SIGN}')
|
||||
await pruning.add_reaction('\N{OCTAGONAL SIGN}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await self.bot.wait_for('reaction_add', check=confirm, timeout=10 * 60)
|
||||
|
||||
deleting = await ctx.send(f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages...**')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
c = 0
|
||||
for channel in ctx.guild.text_channels:
|
||||
await deleting.edit(content=f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages from** {channel.mention}')
|
||||
|
||||
deleted = await channel.purge(check=lambda m: m.author.id == user.id, before=pruning, limit=None)
|
||||
c += len(deleted)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
for channel in ctx.guild.text_channels:
|
||||
missed = 0
|
||||
async for message in channel.history(before=pruning, limit=None):
|
||||
if message.author.id == user.id:
|
||||
missed += 1
|
||||
|
||||
if missed > 0:
|
||||
await ctx.send(f'\N{DOUBLE EXCLAMATION MARK} `{missed}` **messages were not deleted in** {channel.mention}')
|
||||
|
||||
await ctx.send(f'\N{WHITE HEAVY CHECK MARK} **Finished deleting** `{c}` **of** {user.mention}**\'s messages**')
|
||||
|
||||
except exc.Abort:
|
||||
await ctx.send('**Deletion aborted**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
except TimeoutError:
|
||||
await ctx.send('**Deletion timed out**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
|
||||
@cmds.group(aliases=['task', 'tsk'])
|
||||
async def tasks(self):
|
||||
pass
|
||||
|
||||
async def delete(self):
|
||||
while self.deleting:
|
||||
message = await self.queue.get()
|
||||
await asyncio.sleep(self.RATE_LIMIT)
|
||||
with suppress(err.NotFound):
|
||||
if not message.pinned:
|
||||
await message.delete()
|
||||
|
||||
print('STOPPED : deleting')
|
||||
|
||||
async def queue_for_deletion(self, channel):
|
||||
def check(msg):
|
||||
if 'stop d' in msg.content.lower() and msg.channel is channel and msg.author.guild_permissions.administrator:
|
||||
raise exc.Abort
|
||||
elif msg.channel is channel and not msg.pinned:
|
||||
return True
|
||||
return False
|
||||
|
||||
try:
|
||||
async for message in channel.history(limit=None):
|
||||
if 'stop d' in message.content.lower() and message.author.guild_permissions.administrator:
|
||||
raise exc.Abort
|
||||
if not message.pinned:
|
||||
await self.queue.put(message)
|
||||
|
||||
while not self.bot.is_closed():
|
||||
message = await self.bot.wait_for('message', check=check)
|
||||
await self.queue.put(message)
|
||||
|
||||
except exc.Abort:
|
||||
u.tasks['auto_del'].remove(channel.id)
|
||||
u.dump(u.tasks, 'cogs/tasks.pkl')
|
||||
if not u.tasks['auto_del']:
|
||||
self.deleting = False
|
||||
print('STOPPED : deleting #{}'.format(channel.name))
|
||||
await channel.send('**Stopped queueing messages for deletion in** {}'.format(channel.mention), delete_after=5)
|
||||
|
||||
@cmds.command(name='autodelete', aliases=['autodel'])
|
||||
@cmds.has_permissions(administrator=True)
|
||||
async def auto_delete(self, ctx):
|
||||
try:
|
||||
if ctx.channel.id not in u.tasks['auto_del']:
|
||||
u.tasks['auto_del'].append(ctx.channel.id)
|
||||
u.dump(u.tasks, 'cogs/tasks.pkl')
|
||||
self.bot.loop.create_task(self.queue_for_deletion(ctx.channel))
|
||||
if not self.deleting:
|
||||
self.bot.loop.create_task(self.delete())
|
||||
self.deleting = True
|
||||
print('STARTED : auto-deleting in #{}'.format(ctx.channel.name))
|
||||
await ctx.send('**Auto-deleting all messages in {}**'.format(ctx.channel.mention), delete_after=5)
|
||||
else:
|
||||
raise exc.Exists
|
||||
|
||||
except exc.Exists:
|
||||
await ctx.send('**Already auto-deleting in {}.** Type `stop d(eleting)` to stop.'.format(ctx.channel.mention), delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
|
||||
@cmds.group(aliases=['setting', 'set', 's'])
|
||||
@cmds.has_permissions(administrator=True)
|
||||
async def settings(self, ctx):
|
||||
pass
|
||||
|
||||
@settings.command(name='deletecommands', aliases=['delcmds', 'delcmd'])
|
||||
async def _settings_deletecommands(self, ctx):
|
||||
if ctx.guild.id not in u.settings['del_ctx']:
|
||||
u.settings['del_ctx'].append(ctx.guild.id)
|
||||
else:
|
||||
u.settings['del_ctx'].remove(ctx.guild.id)
|
||||
u.dump(u.settings, 'settings.pkl')
|
||||
|
||||
await ctx.send('**Delete command invocations:** `{}`'.format(ctx.guild.id in u.settings['del_ctx']))
|
||||
|
||||
@settings.command(name='prefix', aliases=['pre', 'p'])
|
||||
async def _settings_prefix(self, ctx, *prefixes):
|
||||
if prefixes:
|
||||
u.settings['prefixes'][ctx.guild.id] = prefixes
|
||||
else:
|
||||
with suppress(KeyError):
|
||||
del u.settings['prefixes'][ctx.guild.id]
|
||||
|
||||
await ctx.send(f'**Prefix set to:** `{"` or `".join(prefixes if ctx.guild.id in u.settings["prefixes"] else u.config["prefix"])}`')
|
||||
|
||||
@settings.command(name='deleteresponses', aliases=['delresps', 'delresp'])
|
||||
async def _settings_deleteresponses(self, ctx):
|
||||
if ctx.guild.id not in u.settings['del_resp']:
|
||||
u.settings['del_resp'].append(ctx.guild.id)
|
||||
else:
|
||||
u.settings['del_resp'].remove(ctx.guild.id)
|
||||
u.dump(u.settings, 'settings.pkl')
|
||||
|
||||
await ctx.send(f'**Delete command responses:** `{ctx.guild.id in u.settings["del_resp"]}`')
|
||||
import asyncio
|
||||
import traceback as tb
|
||||
from contextlib import suppress
|
||||
from datetime import datetime as dt
|
||||
|
||||
import discord as d
|
||||
from discord import errors as err
|
||||
from discord.ext import commands as cmds
|
||||
from discord.ext.commands import errors as errext
|
||||
|
||||
from misc import exceptions as exc
|
||||
from misc import checks
|
||||
from utils import utils as u
|
||||
|
||||
|
||||
class Administration:
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.RATE_LIMIT = u.RATE_LIMIT
|
||||
self.queue = asyncio.Queue()
|
||||
self.deleting = False
|
||||
|
||||
if u.tasks['auto_del']:
|
||||
for channel in u.tasks['auto_del']:
|
||||
temp = self.bot.get_channel(channel)
|
||||
self.bot.loop.create_task(self.queue_for_deletion(temp))
|
||||
print('STARTED : auto-deleting in #{}'.format(temp.name))
|
||||
self.deleting = True
|
||||
self.bot.loop.create_task(self.delete())
|
||||
|
||||
@cmds.group(aliases=['pru', 'purge', 'pur', 'clear', 'cl'], hidden=True)
|
||||
@cmds.is_owner()
|
||||
async def prune(self, ctx):
|
||||
pass
|
||||
|
||||
@prune.group(name='user', aliases=['u', 'member', 'm'])
|
||||
async def _prune_user(self, ctx):
|
||||
pass
|
||||
|
||||
@_prune_user.command(name='channel', aliases=['channels', 'chans', 'chan', 'ch', 'c'])
|
||||
async def _prune_user_channel(self, ctx, user: d.User, *channels: d.TextChannel):
|
||||
def confirm(r, u):
|
||||
if u is ctx.author:
|
||||
if r.emoji == '\N{OCTAGONAL SIGN}':
|
||||
raise exc.Abort
|
||||
if r.emoji == '\N{THUMBS UP SIGN}':
|
||||
return True
|
||||
return False
|
||||
|
||||
if not channels:
|
||||
channels = [ctx.channel]
|
||||
|
||||
try:
|
||||
pruning = await ctx.send(f'\N{HOURGLASS} **Pruning** {user.mention}**\'s messages from** {"**,** ".join([channel.mention for channel in channels])} **might take some time.** Proceed, {ctx.author.mention}?')
|
||||
await pruning.add_reaction('\N{THUMBS UP SIGN}')
|
||||
await pruning.add_reaction('\N{OCTAGONAL SIGN}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await self.bot.wait_for('reaction_add', check=confirm, timeout=10 * 60)
|
||||
|
||||
deleting = await ctx.send(f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages...**')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
c = 0
|
||||
for channel in channels:
|
||||
await deleting.edit(content=f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages from** {channel.mention}')
|
||||
|
||||
deleted = await channel.purge(check=lambda m: m.author.id == user.id, before=pruning, limit=None)
|
||||
c += len(deleted)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
for channel in channels:
|
||||
missed = 0
|
||||
async for message in channel.history(before=pruning, limit=None):
|
||||
if message.author.id == user.id:
|
||||
missed += 1
|
||||
|
||||
if missed > 0:
|
||||
await ctx.send(f'\N{DOUBLE EXCLAMATION MARK} `{missed}` **messages were not deleted in** {channel.mention}')
|
||||
|
||||
await ctx.send(f'\N{WHITE HEAVY CHECK MARK} **Finished deleting** `{c}` **of** {user.mention}**\'s messages**')
|
||||
|
||||
except exc.Abort:
|
||||
await ctx.send('**Deletion aborted**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
except TimeoutError:
|
||||
await ctx.send('**Deletion timed out**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
|
||||
@_prune_user.command(name='all', aliases=['a'], brief='Prune a user\'s messages from the guild', description='about flag centers on message 50 of 101 messages\n\npfg \{user id\} [before|after|about] [\{message id\}]\n\nExample:\npfg \{user id\} before \{message id\}', hidden=True)
|
||||
@cmds.is_owner()
|
||||
async def _prune_user_all(self, ctx, user: d.User):
|
||||
def confirm(r, u):
|
||||
if u is ctx.author:
|
||||
if r.emoji == '\N{OCTAGONAL SIGN}':
|
||||
raise exc.Abort
|
||||
if r.emoji == '\N{THUMBS UP SIGN}':
|
||||
return True
|
||||
return False
|
||||
|
||||
try:
|
||||
pruning = await ctx.send(f'\N{HOURGLASS} **Pruning** {user.mention}**\'s messages might take some time.** Proceed, {ctx.author.mention}?')
|
||||
await pruning.add_reaction('\N{THUMBS UP SIGN}')
|
||||
await pruning.add_reaction('\N{OCTAGONAL SIGN}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await self.bot.wait_for('reaction_add', check=confirm, timeout=10 * 60)
|
||||
|
||||
deleting = await ctx.send(f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages...**')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
c = 0
|
||||
for channel in ctx.guild.text_channels:
|
||||
await deleting.edit(content=f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages from** {channel.mention}')
|
||||
|
||||
deleted = await channel.purge(check=lambda m: m.author.id == user.id, before=pruning, limit=None)
|
||||
c += len(deleted)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
for channel in ctx.guild.text_channels:
|
||||
missed = 0
|
||||
async for message in channel.history(before=pruning, limit=None):
|
||||
if message.author.id == user.id:
|
||||
missed += 1
|
||||
|
||||
if missed > 0:
|
||||
await ctx.send(f'\N{DOUBLE EXCLAMATION MARK} `{missed}` **messages were not deleted in** {channel.mention}')
|
||||
|
||||
await ctx.send(f'\N{WHITE HEAVY CHECK MARK} **Finished deleting** `{c}` **of** {user.mention}**\'s messages**')
|
||||
|
||||
except exc.Abort:
|
||||
await ctx.send('**Deletion aborted**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
except TimeoutError:
|
||||
await ctx.send('**Deletion timed out**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
|
||||
@cmds.group(aliases=['task', 'tsk'])
|
||||
async def tasks(self):
|
||||
pass
|
||||
|
||||
async def delete(self):
|
||||
while self.deleting:
|
||||
message = await self.queue.get()
|
||||
await asyncio.sleep(self.RATE_LIMIT)
|
||||
with suppress(err.NotFound):
|
||||
if not message.pinned:
|
||||
await message.delete()
|
||||
|
||||
print('STOPPED : deleting')
|
||||
|
||||
async def queue_for_deletion(self, channel):
|
||||
def check(msg):
|
||||
if 'stop d' in msg.content.lower() and msg.channel is channel and msg.author.guild_permissions.administrator:
|
||||
raise exc.Abort
|
||||
elif msg.channel is channel and not msg.pinned:
|
||||
return True
|
||||
return False
|
||||
|
||||
try:
|
||||
async for message in channel.history(limit=None):
|
||||
if 'stop d' in message.content.lower() and message.author.guild_permissions.administrator:
|
||||
raise exc.Abort
|
||||
if not message.pinned:
|
||||
await self.queue.put(message)
|
||||
|
||||
while not self.bot.is_closed():
|
||||
message = await self.bot.wait_for('message', check=check)
|
||||
await self.queue.put(message)
|
||||
|
||||
except exc.Abort:
|
||||
u.tasks['auto_del'].remove(channel.id)
|
||||
u.dump(u.tasks, 'cogs/tasks.pkl')
|
||||
if not u.tasks['auto_del']:
|
||||
self.deleting = False
|
||||
print('STOPPED : deleting #{}'.format(channel.name))
|
||||
await channel.send('**Stopped queueing messages for deletion in** {}'.format(channel.mention), delete_after=5)
|
||||
|
||||
@cmds.command(name='autodelete', aliases=['autodel'])
|
||||
@cmds.has_permissions(administrator=True)
|
||||
async def auto_delete(self, ctx):
|
||||
try:
|
||||
if ctx.channel.id not in u.tasks['auto_del']:
|
||||
u.tasks['auto_del'].append(ctx.channel.id)
|
||||
u.dump(u.tasks, 'cogs/tasks.pkl')
|
||||
self.bot.loop.create_task(self.queue_for_deletion(ctx.channel))
|
||||
if not self.deleting:
|
||||
self.bot.loop.create_task(self.delete())
|
||||
self.deleting = True
|
||||
print('STARTED : auto-deleting in #{}'.format(ctx.channel.name))
|
||||
await ctx.send('**Auto-deleting all messages in {}**'.format(ctx.channel.mention), delete_after=5)
|
||||
else:
|
||||
raise exc.Exists
|
||||
|
||||
except exc.Exists:
|
||||
await ctx.send('**Already auto-deleting in {}.** Type `stop d(eleting)` to stop.'.format(ctx.channel.mention), delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
|
||||
@cmds.group(aliases=['setting', 'set', 's'])
|
||||
@cmds.has_permissions(administrator=True)
|
||||
async def settings(self, ctx):
|
||||
pass
|
||||
|
||||
@settings.command(name='deletecommands', aliases=['delcmds', 'delcmd'])
|
||||
async def _settings_deletecommands(self, ctx):
|
||||
if ctx.guild.id not in u.settings['del_ctx']:
|
||||
u.settings['del_ctx'].append(ctx.guild.id)
|
||||
else:
|
||||
u.settings['del_ctx'].remove(ctx.guild.id)
|
||||
u.dump(u.settings, 'settings.pkl')
|
||||
|
||||
await ctx.send('**Delete command invocations:** `{}`'.format(ctx.guild.id in u.settings['del_ctx']))
|
||||
|
||||
@settings.command(name='prefix', aliases=['pre', 'p'])
|
||||
async def _settings_prefix(self, ctx, *prefixes):
|
||||
if prefixes:
|
||||
u.settings['prefixes'][ctx.guild.id] = prefixes
|
||||
else:
|
||||
with suppress(KeyError):
|
||||
del u.settings['prefixes'][ctx.guild.id]
|
||||
|
||||
await ctx.send(f'**Prefix set to:** `{"` or `".join(prefixes if ctx.guild.id in u.settings["prefixes"] else u.config["prefix"])}`')
|
||||
|
||||
@settings.command(name='deleteresponses', aliases=['delresps', 'delresp'])
|
||||
async def _settings_deleteresponses(self, ctx):
|
||||
if ctx.guild.id not in u.settings['del_resp']:
|
||||
u.settings['del_resp'].append(ctx.guild.id)
|
||||
else:
|
||||
u.settings['del_resp'].remove(ctx.guild.id)
|
||||
u.dump(u.settings, 'settings.pkl')
|
||||
|
||||
await ctx.send(f'**Delete command responses:** `{ctx.guild.id in u.settings["del_resp"]}`')
|
||||
|
|
538
src/run.py
538
src/run.py
|
@ -1,266 +1,272 @@
|
|||
import asyncio
|
||||
from datetime import datetime as dt
|
||||
import json
|
||||
import logging as log
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback as tb
|
||||
from contextlib import suppress
|
||||
from pprint import pprint
|
||||
from hurry.filesize import size, alternative
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import discord as d
|
||||
from discord import errors as err
|
||||
from discord import utils
|
||||
from discord.ext import commands as cmds
|
||||
from discord.ext.commands import errors as errext
|
||||
|
||||
from misc import exceptions as exc
|
||||
from misc import checks
|
||||
from utils import utils as u
|
||||
|
||||
log.basicConfig(level=log.WARNING)
|
||||
|
||||
|
||||
# class HelpFormatter(cmds.HelpFormatter):
|
||||
#
|
||||
# async def format(self):
|
||||
# self._paginator = cmds.Paginator()
|
||||
#
|
||||
# # we need a padding of ~80 or so
|
||||
#
|
||||
# description = self.command.description if not self.is_cog() else inspect.getdoc(self.command)
|
||||
#
|
||||
# if description:
|
||||
# # <description> portion
|
||||
# self._paginator.add_line(description, empty=True)
|
||||
#
|
||||
# if isinstance(self.command, cmds.Command):
|
||||
# # <signature portion>
|
||||
# signature = self.get_command_signature()
|
||||
# self._paginator.add_line(signature, empty=True)
|
||||
#
|
||||
# # <long doc> section
|
||||
# if self.command.help:
|
||||
# self._paginator.add_line(self.command.help, empty=True)
|
||||
#
|
||||
# # end it here if it's just a regular command
|
||||
# if not self.has_subcommands():
|
||||
# self._paginator.close_page()
|
||||
# return self._paginator.pages
|
||||
#
|
||||
# max_width = self.max_name_size
|
||||
|
||||
|
||||
def get_prefix(bot, message):
|
||||
with suppress(AttributeError):
|
||||
return u.settings['prefixes'].get(message.guild.id, u.config['prefix'])
|
||||
return u.config['prefix']
|
||||
|
||||
bot = cmds.Bot(command_prefix=get_prefix, self_bot=u.config['selfbot'], formatter=cmds.HelpFormatter(show_check_failure=True), description='Modufur - A booru bot with a side of management and automated tasking\nMade by @Myned#3985\n\nNSFW for Not Safe For Wumpus commands\n(G) for group commands\n@permission@ for required permissions\n!notice! for important information\np for prefix\n\n\{\} for mandatory argument\n[] for optional argument\n... for one or more arguments', help_attrs={'aliases': ['h']}, pm_help=None)
|
||||
|
||||
@bot.command(help='help', brief='brief', description='description', usage='usage', hidden=True)
|
||||
async def test(ctx):
|
||||
await ctx.send('test')
|
||||
|
||||
# Send and print ready message to #testing and console after logon
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
if not checks.ready:
|
||||
# d.opus.load_opus('opuslib')
|
||||
|
||||
from cogs import booru, info, management, owner, tools
|
||||
|
||||
for cog in (tools.Utils(bot), owner.Bot(bot), owner.Tools(bot), management.Administration(bot), info.Info(bot), booru.MsG(bot)):
|
||||
bot.add_cog(cog)
|
||||
print(f'COG : {type(cog).__name__}')
|
||||
|
||||
# bot.loop.create_task(u.clear(booru.temp_urls, 30*60))
|
||||
|
||||
if u.config['playing'] is not '':
|
||||
await bot.change_presence(game=d.Game(name=u.config['playing']))
|
||||
|
||||
print('\n> > > > > > > > >\nC O N N E C T E D : {}\n> > > > > > > > >\n'.format(bot.user.name))
|
||||
await bot.get_channel(u.config['info_channel']).send(f'**Started** \N{BLACK SUN WITH RAYS} `{"` or `".join(u.config["prefix"])}`')
|
||||
# u.notify('C O N N E C T E D')
|
||||
|
||||
if u.temp['startup']:
|
||||
with suppress(err.NotFound):
|
||||
if u.temp['startup'][0] == 'guild':
|
||||
dest = bot.get_channel(u.temp['startup'][1])
|
||||
else:
|
||||
dest = bot.get_user(u.temp['startup'][1])
|
||||
message = await dest.get_message(u.temp['startup'][2])
|
||||
|
||||
await message.add_reaction('\N{WHITE HEAVY CHECK MARK}')
|
||||
|
||||
u.temp['startup'] = ()
|
||||
u.dump(u.temp, 'temp/temp.pkl')
|
||||
|
||||
checks.ready = True
|
||||
else:
|
||||
print('\n- - - -\nI N F O : reconnected, skipping initialization\n- - - -')
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
if not u.config['selfbot']:
|
||||
if message.author is not bot.user and not message.author.bot:
|
||||
await bot.process_commands(message)
|
||||
else:
|
||||
if not message.author.bot:
|
||||
await bot.process_commands(message)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_error(error, *args, **kwargs):
|
||||
print('\n! ! ! ! !\nE R R O R : {}\n! ! ! ! !\n'.format(error), file=sys.stderr)
|
||||
tb.print_exc()
|
||||
await bot.get_user(u.config['owner_id']).send('**ERROR** \N{WARNING SIGN}\n```\n{}```'.format(error))
|
||||
await bot.get_channel(u.config['info_channel']).send('**ERROR** \N{WARNING SIGN}\n```\n{}```'.format(error))
|
||||
|
||||
if u.temp['startup']:
|
||||
with suppress(err.NotFound):
|
||||
if u.temp['startup'][0] == 'guild':
|
||||
dest = bot.get_channel(u.temp['startup'][1])
|
||||
else:
|
||||
dest = bot.get_user(u.temp['startup'][1])
|
||||
message = await dest.get_message(u.temp['startup'][2])
|
||||
|
||||
await message.add_reaction('\N{WARNING SIGN}')
|
||||
|
||||
u.temp.clear()
|
||||
u.dump(u.temp, 'temp/temp.pkl')
|
||||
# u.notify('E R R O R')
|
||||
await bot.logout()
|
||||
u.close(bot.loop)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_command_error(ctx, error):
|
||||
if isinstance(error, err.NotFound):
|
||||
print('NOT FOUND')
|
||||
elif isinstance(error, errext.MissingRequiredArgument):
|
||||
await ctx.send('**Missing required argument**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
elif isinstance(error, errext.BadArgument):
|
||||
await ctx.send(f'**Invalid argument.** {error}', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
elif isinstance(error, errext.CheckFailure):
|
||||
await ctx.send('**Insufficient permissions**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{NO ENTRY}')
|
||||
elif isinstance(error, errext.CommandNotFound):
|
||||
print('INVALID COMMAND : {}'.format(error), file=sys.stderr)
|
||||
await ctx.message.add_reaction('\N{BLACK QUESTION MARK ORNAMENT}')
|
||||
else:
|
||||
print('\n! ! ! ! ! ! ! ! ! ! ! !\nC O M M A N D E R R O R : {}\n! ! ! ! ! ! ! ! ! ! ! !\n'.format(
|
||||
error), file=sys.stderr)
|
||||
tb.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
|
||||
await bot.get_user(u.config['owner_id']).send('**COMMAND ERROR** \N{WARNING SIGN} `{}` from {} in {}\n```\n{}```'.format(ctx.message.content, ctx.author.mention, ctx.channel.mention, ''.join(tb.format_exception(type(error), error, error.__traceback__ if len(str(error.__traceback__)) < 1500 else str(error.__traceback__)[:1500]))))
|
||||
await bot.get_channel(u.config['info_channel']).send('**COMMAND ERROR** \N{WARNING SIGN} `{}` from {} in {}\n```\n{}```'.format(ctx.message.content, ctx.author.mention, ctx.channel.mention, error))
|
||||
await exc.send_error(ctx, error)
|
||||
await ctx.message.add_reaction('\N{WARNING SIGN}')
|
||||
# u.notify('C O M M A N D E R R O R')
|
||||
|
||||
# @bot.event
|
||||
# async def on_command(ctx):
|
||||
# if ctx.guild.id in u.settings['del_resp']:
|
||||
# pass
|
||||
|
||||
@bot.event
|
||||
async def on_command_completion(ctx):
|
||||
with suppress(err.NotFound):
|
||||
with suppress(AttributeError):
|
||||
if ctx.guild.id in u.settings['del_ctx'] and ctx.me.permissions_in(ctx.channel).manage_messages and isinstance(ctx.message.channel, d.TextChannel):
|
||||
await ctx.message.delete()
|
||||
|
||||
await ctx.message.add_reaction('\N{WHITE HEAVY CHECK MARK}')
|
||||
|
||||
for command in ('lastcommand', ',restart', ',die'):
|
||||
if ctx.command.name == command:
|
||||
return
|
||||
|
||||
u.last_commands[ctx.author.id] = ctx
|
||||
|
||||
@bot.event
|
||||
async def on_guild_remove(guild):
|
||||
print(f'LEFT : {guild.name}')
|
||||
|
||||
for task, idents in u.tasks.items():
|
||||
for channel in guild.channels:
|
||||
if channel.id in idents:
|
||||
idents.remove(channel.id)
|
||||
print(f'STOPPED : {task} in #{channel.id}')
|
||||
u.dump(u.tasks, 'cogs/tasks.pkl')
|
||||
|
||||
|
||||
async def wait(voice):
|
||||
asyncio.sleep(5)
|
||||
await voice.disconnect()
|
||||
|
||||
|
||||
def after(voice, error):
|
||||
coro = voice.disconnect()
|
||||
future = asyncio.run_coroutine_threadsafe(coro, voice.loop)
|
||||
future.result()
|
||||
|
||||
# suggested = u.setdefault('cogs/suggested.pkl', {'last_update': 'None', 'tags': {}, 'total': 0})
|
||||
@bot.command(name=',test', hidden=True)
|
||||
@cmds.is_owner()
|
||||
async def test(ctx):
|
||||
post = await u.fetch('https://e621.net/post/show.json?id=1145042', json=True)
|
||||
|
||||
tags = []
|
||||
if post['tags']:
|
||||
temptags = post['tags'].split(' ')
|
||||
cis = []
|
||||
for tag in suggested:
|
||||
pass
|
||||
for tag in temptags:
|
||||
tags.append(f'[{tag}](https://e621.net/post?tags={tag})')
|
||||
# tags = ' '.join(tags)
|
||||
else:
|
||||
tags = 'None'
|
||||
|
||||
if post['description']:
|
||||
post_description = post['description'] if len(post['description']) < 200 else f'{post["description"][:200]}...'
|
||||
else:
|
||||
post_description = 'None'
|
||||
|
||||
title = ', '.join(post['artist'])
|
||||
description = f'posted by: *[{post["author"]}](https://e621.net/post?tags=user:{post["author"]})*'
|
||||
url = f'https://e621.net/post?tags={",".join(post["artist"])}'
|
||||
# timestamp = dt.utcnow()
|
||||
color = ctx.me.color
|
||||
footer = {'text': post['score'], 'icon_url': 'https://images-ext-1.discordapp.net/external/W2k0ZzhU7ngvN_-CdqAa3H3FmkfCNYQTxPG_DsvacB4/https/emojipedia-us.s3.amazonaws.com/thumbs/320/twitter/103/sparkles_2728.png'}
|
||||
# image = 'https://e621.net/post/show/54360'
|
||||
thumbnail = post['file_url']
|
||||
author = {'name': post['id'], 'url': f'https://e621.net/post/show/{post["id"]}', 'icon_url': ctx.author.avatar_url}
|
||||
|
||||
fields = []
|
||||
names = ('File', 'Sources', 'Description', 'tags', 'tags (ext.)')
|
||||
values = (f'[{post["md5"]}]({post["file_url"]}) | [{post["file_ext"]}](https://e621.net/post?tags=type:{post["file_ext"]})\n\n**Size** [{size(post["file_size"], system=alternative)}](https://e621.net/post?tags=filesize:{post["file_size"]})\n**Resolution** [{post["width"]} x {post["height"]}](https://e621.net/post?tags=width:{post["width"]},height:{post["height"]}) | [{u.get_aspectratio(post["width"], post["height"])}](https://e621.net/post?tags=ratio:{post["width"]/post["height"]:.2f})', '\n'.join([f'[{urlparse(source).netloc}]({source})' for source in post['sources']]), post_description, ' '.join(tags[:20]), ' '.join(tags[20:]))
|
||||
inlines = (False, False, False, True, True)
|
||||
for name, value, inline in zip(names, values, inlines):
|
||||
fields.append({'name': name, 'value': value, 'inline': inline})
|
||||
|
||||
embed = u.generate_embed(ctx, title=title, description=description, url=url, colour=color, footer=footer, thumbnail=thumbnail, author=author, fields=fields)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
# print(ctx.args)
|
||||
# print(ctx.kwargs)
|
||||
# if '<:N_:368917475531816962>' in message:
|
||||
# await ctx.send('<:N_:368917475531816962>')
|
||||
# logs = []
|
||||
# async for entry in ctx.guild.audit_logs(limit=None, action=d.AuditLogAction.message_delete):
|
||||
# logs.append(
|
||||
# f'@{entry.user.name} deleted {entry.extra.count} messages from @{entry.target.name} in #{entry.extra.channel.name}')
|
||||
# pprint(logs)
|
||||
# channel = bot.get_channel(int(cid))
|
||||
# voice = await channel.connect()
|
||||
# voice.play(d.AudioSource, after=lambda: after(voice))
|
||||
|
||||
bot.run(u.config['token'], bot=not u.config['selfbot'])
|
||||
import asyncio
|
||||
from datetime import datetime as dt
|
||||
import json
|
||||
import logging as log
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback as tb
|
||||
from contextlib import suppress
|
||||
from pprint import pprint
|
||||
from hurry.filesize import size, alternative
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import discord as d
|
||||
from discord import errors as err
|
||||
from discord import utils
|
||||
from discord.ext import commands as cmds
|
||||
from discord.ext.commands import errors as errext
|
||||
|
||||
from misc import exceptions as exc
|
||||
from misc import checks
|
||||
from utils import utils as u
|
||||
|
||||
log.basicConfig(level=log.WARNING)
|
||||
|
||||
|
||||
# class HelpFormatter(cmds.HelpFormatter):
|
||||
#
|
||||
# async def format(self):
|
||||
# self._paginator = cmds.Paginator()
|
||||
#
|
||||
# # we need a padding of ~80 or so
|
||||
#
|
||||
# description = self.command.description if not self.is_cog() else inspect.getdoc(self.command)
|
||||
#
|
||||
# if description:
|
||||
# # <description> portion
|
||||
# self._paginator.add_line(description, empty=True)
|
||||
#
|
||||
# if isinstance(self.command, cmds.Command):
|
||||
# # <signature portion>
|
||||
# signature = self.get_command_signature()
|
||||
# self._paginator.add_line(signature, empty=True)
|
||||
#
|
||||
# # <long doc> section
|
||||
# if self.command.help:
|
||||
# self._paginator.add_line(self.command.help, empty=True)
|
||||
#
|
||||
# # end it here if it's just a regular command
|
||||
# if not self.has_subcommands():
|
||||
# self._paginator.close_page()
|
||||
# return self._paginator.pages
|
||||
#
|
||||
# max_width = self.max_name_size
|
||||
|
||||
|
||||
def get_prefix(bot, message):
|
||||
with suppress(AttributeError):
|
||||
return u.settings['prefixes'].get(message.guild.id, u.config['prefix'])
|
||||
return u.config['prefix']
|
||||
|
||||
bot = cmds.Bot(command_prefix=get_prefix, self_bot=u.config['selfbot'], formatter=cmds.HelpFormatter(show_check_failure=True), description='Modufur - A booru bot with a side of management and automated tasking\nMade by @Myned#3985\n\nNSFW for Not Safe For Wumpus commands\n(G) for group commands\n@permission@ for required permissions\n!notice! for important information\np for prefix\n\n\{\} for mandatory argument\n[] for optional argument\n... for one or more arguments', help_attrs={'aliases': ['h']}, pm_help=None)
|
||||
|
||||
@bot.command(help='help', brief='brief', description='description', usage='usage', hidden=True)
|
||||
async def test(ctx):
|
||||
await ctx.send('test')
|
||||
|
||||
# Send and print ready message to #testing and console after logon
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
if not checks.ready:
|
||||
# d.opus.load_opus('opuslib')
|
||||
|
||||
from cogs import booru, info, management, owner, tools
|
||||
|
||||
for cog in (tools.Utils(bot), owner.Bot(bot), owner.Tools(bot), management.Administration(bot), info.Info(bot), booru.MsG(bot)):
|
||||
bot.add_cog(cog)
|
||||
print(f'COG : {type(cog).__name__}')
|
||||
|
||||
# bot.loop.create_task(u.clear(booru.temp_urls, 30*60))
|
||||
|
||||
if u.config['playing'] is not '':
|
||||
await bot.change_presence(game=d.Game(name=u.config['playing']))
|
||||
|
||||
print('\n> > > > > > > > >\nC O N N E C T E D : {}\n> > > > > > > > >\n'.format(bot.user.name))
|
||||
await bot.get_channel(u.config['info_channel']).send(f'**Started** \N{BLACK SUN WITH RAYS} `{"` or `".join(u.config["prefix"])}`')
|
||||
# u.notify('C O N N E C T E D')
|
||||
|
||||
if u.temp['startup']:
|
||||
with suppress(err.NotFound):
|
||||
if u.temp['startup'][0] == 'guild':
|
||||
dest = bot.get_channel(u.temp['startup'][1])
|
||||
else:
|
||||
dest = bot.get_user(u.temp['startup'][1])
|
||||
message = await dest.get_message(u.temp['startup'][2])
|
||||
|
||||
await message.add_reaction('\N{WHITE HEAVY CHECK MARK}')
|
||||
|
||||
u.temp['startup'] = ()
|
||||
u.dump(u.temp, 'temp/temp.pkl')
|
||||
|
||||
checks.ready = True
|
||||
else:
|
||||
print('\n- - - -\nI N F O : reconnected, reinitializing\n- - - -')
|
||||
|
||||
for cog in (tools.Utils(bot), owner.Bot(bot), owner.Tools(bot), management.Administration(bot), info.Info(bot), booru.MsG(bot)):
|
||||
bot.add_cog(cog)
|
||||
print(f'COG : {type(cog).__name__}')
|
||||
|
||||
if u.config['playing'] is not '':
|
||||
await bot.change_presence(game=d.Game(name=u.config['playing']))
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
if not u.config['selfbot']:
|
||||
if message.author is not bot.user and not message.author.bot:
|
||||
await bot.process_commands(message)
|
||||
else:
|
||||
if not message.author.bot:
|
||||
await bot.process_commands(message)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_error(error, *args, **kwargs):
|
||||
print('\n! ! ! ! !\nE R R O R : {}\n! ! ! ! !\n'.format(error), file=sys.stderr)
|
||||
tb.print_exc()
|
||||
await bot.get_user(u.config['owner_id']).send('**ERROR** \N{WARNING SIGN}\n```\n{}```'.format(error))
|
||||
await bot.get_channel(u.config['info_channel']).send('**ERROR** \N{WARNING SIGN}\n```\n{}```'.format(error))
|
||||
|
||||
if u.temp['startup']:
|
||||
with suppress(err.NotFound):
|
||||
if u.temp['startup'][0] == 'guild':
|
||||
dest = bot.get_channel(u.temp['startup'][1])
|
||||
else:
|
||||
dest = bot.get_user(u.temp['startup'][1])
|
||||
message = await dest.get_message(u.temp['startup'][2])
|
||||
|
||||
await message.add_reaction('\N{WARNING SIGN}')
|
||||
|
||||
u.temp.clear()
|
||||
u.dump(u.temp, 'temp/temp.pkl')
|
||||
# u.notify('E R R O R')
|
||||
await bot.logout()
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_command_error(ctx, error):
|
||||
if isinstance(error, err.NotFound):
|
||||
print('NOT FOUND')
|
||||
elif isinstance(error, errext.MissingRequiredArgument):
|
||||
await ctx.send('**Missing required argument**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
elif isinstance(error, errext.BadArgument):
|
||||
await ctx.send(f'**Invalid argument.** {error}', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{CROSS MARK}')
|
||||
elif isinstance(error, errext.CheckFailure):
|
||||
await ctx.send('**Insufficient permissions**', delete_after=7)
|
||||
await ctx.message.add_reaction('\N{NO ENTRY}')
|
||||
elif isinstance(error, errext.CommandNotFound):
|
||||
print('INVALID COMMAND : {}'.format(error), file=sys.stderr)
|
||||
await ctx.message.add_reaction('\N{BLACK QUESTION MARK ORNAMENT}')
|
||||
else:
|
||||
print('\n! ! ! ! ! ! ! ! ! ! ! !\nC O M M A N D E R R O R : {}\n! ! ! ! ! ! ! ! ! ! ! !\n'.format(
|
||||
error), file=sys.stderr)
|
||||
tb.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
|
||||
await bot.get_user(u.config['owner_id']).send('**COMMAND ERROR** \N{WARNING SIGN} `{}` from {} in {}\n```\n{}```'.format(ctx.message.content, ctx.author.mention, ctx.channel.mention, ''.join(tb.format_exception(type(error), error, error.__traceback__ if len(str(error.__traceback__)) < 1500 else str(error.__traceback__)[:1500]))))
|
||||
await bot.get_channel(u.config['info_channel']).send('**COMMAND ERROR** \N{WARNING SIGN} `{}` from {} in {}\n```\n{}```'.format(ctx.message.content, ctx.author.mention, ctx.channel.mention, error))
|
||||
await exc.send_error(ctx, error)
|
||||
await ctx.message.add_reaction('\N{WARNING SIGN}')
|
||||
# u.notify('C O M M A N D E R R O R')
|
||||
|
||||
# @bot.event
|
||||
# async def on_command(ctx):
|
||||
# if ctx.guild.id in u.settings['del_resp']:
|
||||
# pass
|
||||
|
||||
@bot.event
|
||||
async def on_command_completion(ctx):
|
||||
with suppress(err.NotFound):
|
||||
with suppress(AttributeError):
|
||||
if ctx.guild.id in u.settings['del_ctx'] and ctx.me.permissions_in(ctx.channel).manage_messages and isinstance(ctx.message.channel, d.TextChannel):
|
||||
await ctx.message.delete()
|
||||
|
||||
await ctx.message.add_reaction('\N{WHITE HEAVY CHECK MARK}')
|
||||
|
||||
for command in ('lastcommand', ',restart', ',die'):
|
||||
if ctx.command.name == command:
|
||||
return
|
||||
|
||||
u.last_commands[ctx.author.id] = ctx
|
||||
|
||||
@bot.event
|
||||
async def on_guild_remove(guild):
|
||||
print(f'LEFT : {guild.name}')
|
||||
|
||||
for task, idents in u.tasks.items():
|
||||
for channel in guild.channels:
|
||||
if channel.id in idents:
|
||||
idents.remove(channel.id)
|
||||
print(f'STOPPED : {task} in #{channel.id}')
|
||||
u.dump(u.tasks, 'cogs/tasks.pkl')
|
||||
|
||||
|
||||
async def wait(voice):
|
||||
asyncio.sleep(5)
|
||||
await voice.disconnect()
|
||||
|
||||
|
||||
def after(voice, error):
|
||||
coro = voice.disconnect()
|
||||
future = asyncio.run_coroutine_threadsafe(coro, voice.loop)
|
||||
future.result()
|
||||
|
||||
# suggested = u.setdefault('cogs/suggested.pkl', {'last_update': 'None', 'tags': {}, 'total': 0})
|
||||
@bot.command(name=',test', hidden=True)
|
||||
@cmds.is_owner()
|
||||
async def test(ctx):
|
||||
post = await u.fetch('https://e621.net/post/show.json?id=1145042', json=True)
|
||||
|
||||
tags = []
|
||||
if post['tags']:
|
||||
temptags = post['tags'].split(' ')
|
||||
cis = []
|
||||
for tag in suggested:
|
||||
pass
|
||||
for tag in temptags:
|
||||
tags.append(f'[{tag}](https://e621.net/post?tags={tag})')
|
||||
# tags = ' '.join(tags)
|
||||
else:
|
||||
tags = 'None'
|
||||
|
||||
if post['description']:
|
||||
post_description = post['description'] if len(post['description']) < 200 else f'{post["description"][:200]}...'
|
||||
else:
|
||||
post_description = 'None'
|
||||
|
||||
title = ', '.join(post['artist'])
|
||||
description = f'posted by: *[{post["author"]}](https://e621.net/post?tags=user:{post["author"]})*'
|
||||
url = f'https://e621.net/post?tags={",".join(post["artist"])}'
|
||||
# timestamp = dt.utcnow()
|
||||
color = ctx.me.color
|
||||
footer = {'text': post['score'], 'icon_url': 'https://images-ext-1.discordapp.net/external/W2k0ZzhU7ngvN_-CdqAa3H3FmkfCNYQTxPG_DsvacB4/https/emojipedia-us.s3.amazonaws.com/thumbs/320/twitter/103/sparkles_2728.png'}
|
||||
# image = 'https://e621.net/post/show/54360'
|
||||
thumbnail = post['file_url']
|
||||
author = {'name': post['id'], 'url': f'https://e621.net/post/show/{post["id"]}', 'icon_url': ctx.author.avatar_url}
|
||||
|
||||
fields = []
|
||||
names = ('File', 'Sources', 'Description', 'tags', 'tags (ext.)')
|
||||
values = (f'[{post["md5"]}]({post["file_url"]}) | [{post["file_ext"]}](https://e621.net/post?tags=type:{post["file_ext"]})\n\n**Size** [{size(post["file_size"], system=alternative)}](https://e621.net/post?tags=filesize:{post["file_size"]})\n**Resolution** [{post["width"]} x {post["height"]}](https://e621.net/post?tags=width:{post["width"]},height:{post["height"]}) | [{u.get_aspectratio(post["width"], post["height"])}](https://e621.net/post?tags=ratio:{post["width"]/post["height"]:.2f})', '\n'.join([f'[{urlparse(source).netloc}]({source})' for source in post['sources']]), post_description, ' '.join(tags[:20]), ' '.join(tags[20:]))
|
||||
inlines = (False, False, False, True, True)
|
||||
for name, value, inline in zip(names, values, inlines):
|
||||
fields.append({'name': name, 'value': value, 'inline': inline})
|
||||
|
||||
embed = u.generate_embed(ctx, title=title, description=description, url=url, colour=color, footer=footer, thumbnail=thumbnail, author=author, fields=fields)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
# print(ctx.args)
|
||||
# print(ctx.kwargs)
|
||||
# if '<:N_:368917475531816962>' in message:
|
||||
# await ctx.send('<:N_:368917475531816962>')
|
||||
# logs = []
|
||||
# async for entry in ctx.guild.audit_logs(limit=None, action=d.AuditLogAction.message_delete):
|
||||
# logs.append(
|
||||
# f'@{entry.user.name} deleted {entry.extra.count} messages from @{entry.target.name} in #{entry.extra.channel.name}')
|
||||
# pprint(logs)
|
||||
# channel = bot.get_channel(int(cid))
|
||||
# voice = await channel.connect()
|
||||
# voice.play(d.AudioSource, after=lambda: after(voice))
|
||||
|
||||
bot.run(u.config['token'], bot=not u.config['selfbot'])
|
||||
|
|
|
@ -1,191 +1,191 @@
|
|||
import asyncio
|
||||
import json as jsn
|
||||
import os
|
||||
import pickle as pkl
|
||||
import subprocess
|
||||
from contextlib import suppress
|
||||
from fractions import gcd
|
||||
import math
|
||||
import gmusicapi as gpm
|
||||
|
||||
import aiohttp
|
||||
import discord as d
|
||||
|
||||
from misc import exceptions as exc
|
||||
|
||||
# from pync import Notifier
|
||||
|
||||
|
||||
print('\nPID : {}\n'.format(os.getpid()))
|
||||
|
||||
|
||||
# def notify(message):
|
||||
# subprocess.run(['terminal-notifier', '-message', message, '-title',
|
||||
# 'Modumind', '-activate', 'com.apple.Terminal', '-appIcon', 'icon.png', '-sound', 'Ping'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
|
||||
try:
|
||||
with open('config.json') as infile:
|
||||
config = jsn.load(infile)
|
||||
print('LOADED : config.json')
|
||||
|
||||
except FileNotFoundError:
|
||||
with open('config.json', 'w') as outfile:
|
||||
jsn.dump({'client_id': 0, 'info_channel': 0, 'owner_id': 0, 'permissions': 126016,
|
||||
'playing': 'a game', 'prefix': [',', 'm,'], 'selfbot': False, 'token': 'str'}, outfile, indent=4, sort_keys=True)
|
||||
print('FILE NOT FOUND : config.json created with abstract values. Restart run.py with correct values')
|
||||
|
||||
|
||||
def setdefault(filename, default=None, json=False):
|
||||
if json:
|
||||
try:
|
||||
with open(filename, 'r') as infile:
|
||||
print(f'LOADED : {filename}')
|
||||
return jsn.load(infile)
|
||||
|
||||
except FileNotFoundError:
|
||||
with open(filename, 'w+') as iofile:
|
||||
print(f'FILE NOT FOUND : {filename} created and loaded with default values')
|
||||
jsn.dump(default, iofile)
|
||||
iofile.seek(0)
|
||||
return jsn.load(iofile)
|
||||
else:
|
||||
try:
|
||||
with open(filename, 'rb') as infile:
|
||||
print(f'LOADED : {filename}')
|
||||
return pkl.load(infile)
|
||||
|
||||
except FileNotFoundError:
|
||||
with open(filename, 'wb+') as iofile:
|
||||
print(f'FILE NOT FOUND : {filename} created and loaded with default values')
|
||||
pkl.dump(default, iofile)
|
||||
iofile.seek(0)
|
||||
return pkl.load(iofile)
|
||||
|
||||
|
||||
def load(filename, *, json=False):
|
||||
if not json:
|
||||
with open(filename, 'rb') as infile:
|
||||
return pkl.load(infile)
|
||||
else:
|
||||
with open(filename) as infile:
|
||||
return jsn.load(infile)
|
||||
|
||||
|
||||
def dump(obj, filename, *, json=False):
|
||||
if not json:
|
||||
with open(filename, 'wb') as outfile:
|
||||
pkl.dump(obj, outfile)
|
||||
else:
|
||||
with open(filename, 'w') as outfile:
|
||||
jsn.dump(obj, outfile, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
settings = setdefault('misc/settings.pkl', default={'del_ctx': [], 'del_resp': [], 'prefixes': {}})
|
||||
tasks = setdefault('cogs/tasks.pkl', default={'auto_del': [], 'auto_hrt': [], 'auto_rev': [], 'periodic_gpm': []})
|
||||
temp = setdefault('temp/temp.pkl', default={'startup': ()})
|
||||
secrets = setdefault('secrets.json', default={'client_secrets': {'client_id': '', 'client_secret': ''}}, json=True)
|
||||
|
||||
RATE_LIMIT = 2.2
|
||||
color = d.Color(0x1A1A1A)
|
||||
session = aiohttp.ClientSession()
|
||||
last_commands = {}
|
||||
|
||||
|
||||
async def fetch(url, *, params={}, json=False, response=False):
|
||||
async with session.get(url, params=params, headers={'User-Agent': 'Myned/Modufur'}) as r:
|
||||
if response:
|
||||
return r
|
||||
elif json:
|
||||
return await r.json()
|
||||
return await r.read()
|
||||
|
||||
|
||||
# async def clear(obj, interval=10 * 60, replace=None):
|
||||
# if replace is None:
|
||||
# if type(obj) is list:
|
||||
# replace = []
|
||||
# elif type(obj) is dict:
|
||||
# replace = {}
|
||||
# elif type(obj) is int:
|
||||
# replace = 0
|
||||
# elif type(obj) is str:
|
||||
# replace = ''
|
||||
#
|
||||
# while True:
|
||||
# obj = replace
|
||||
# asyncio.sleep(interval)
|
||||
|
||||
|
||||
def close(loop):
|
||||
if session:
|
||||
session.close()
|
||||
|
||||
loop.stop()
|
||||
pending = asyncio.Task.all_tasks()
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
# with suppress(asyncio.CancelledError):
|
||||
# loop.run_until_complete(task)
|
||||
# loop.close()
|
||||
|
||||
print('Finished cancelling tasks.')
|
||||
|
||||
|
||||
def generate_embed(ctx, *, title=d.Embed.Empty, kind='rich', description=d.Embed.Empty, url=d.Embed.Empty, timestamp=d.Embed.Empty, colour=color, footer={}, image=d.Embed.Empty, thumbnail=d.Embed.Empty, author={}, fields=[]):
|
||||
embed = d.Embed(title=title, type=kind, description=description, url=url, timestamp=timestamp, colour=colour if isinstance(ctx.channel, d.TextChannel) else color)
|
||||
|
||||
if footer:
|
||||
embed.set_footer(text=footer.get('text', d.Embed.Empty), icon_url=footer.get('icon_url', d.Embed.Empty))
|
||||
if image:
|
||||
embed.set_image(url=image)
|
||||
if thumbnail:
|
||||
embed.set_thumbnail(url=thumbnail)
|
||||
if author:
|
||||
embed.set_author(name=author.get('name', d.Embed.Empty), url=author.get('url', d.Embed.Empty), icon_url=author.get('icon_url', d.Embed.Empty))
|
||||
for field in fields:
|
||||
embed.add_field(name=field.get('name', d.Embed.Empty), value=field.get('value', d.Embed.Empty), inline=field.get('inline', True))
|
||||
|
||||
return embed
|
||||
|
||||
def get_kwargs(ctx, args, *, limit=False):
|
||||
destination = ctx
|
||||
remaining = list(args[:])
|
||||
rm = False
|
||||
lim = 1
|
||||
|
||||
for flag in ('-d', '-dm'):
|
||||
if flag in remaining:
|
||||
destination = ctx.author
|
||||
|
||||
remaining.remove(flag)
|
||||
|
||||
for flag in ('-r', '-rm', '-remove', '-re', '-repl', '-replace'):
|
||||
if flag in remaining and ctx.author.permissions_in(ctx.channel).manage_messages:
|
||||
rm = True
|
||||
|
||||
remaining.remove(flag)
|
||||
|
||||
if limit:
|
||||
for arg in remaining:
|
||||
if arg.isdigit():
|
||||
if 1 <= int(arg) <= limit:
|
||||
lim = int(arg)
|
||||
remaining.remove(arg)
|
||||
break
|
||||
else:
|
||||
raise exc.BoundsError(arg)
|
||||
|
||||
return {'destination': destination, 'remaining': remaining, 'remove': rm, 'limit': lim}
|
||||
|
||||
|
||||
def get_aspectratio(a, b):
|
||||
divisor = gcd(a, b)
|
||||
return f'{int(a / divisor)}:{int(b / divisor)}'
|
||||
|
||||
|
||||
def ci(pos, n):
|
||||
z = 1.96
|
||||
phat = float(pos) / n
|
||||
|
||||
return (phat + z*z/(2*n) - z * math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
|
||||
import asyncio
|
||||
import json as jsn
|
||||
import os
|
||||
import pickle as pkl
|
||||
import subprocess
|
||||
from contextlib import suppress
|
||||
from fractions import gcd
|
||||
import math
|
||||
import gmusicapi as gpm
|
||||
|
||||
import aiohttp
|
||||
import discord as d
|
||||
|
||||
from misc import exceptions as exc
|
||||
|
||||
# from pync import Notifier
|
||||
|
||||
|
||||
print('\nPID : {}\n'.format(os.getpid()))
|
||||
|
||||
|
||||
# def notify(message):
|
||||
# subprocess.run(['terminal-notifier', '-message', message, '-title',
|
||||
# 'Modumind', '-activate', 'com.apple.Terminal', '-appIcon', 'icon.png', '-sound', 'Ping'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
|
||||
try:
|
||||
with open('config.json') as infile:
|
||||
config = jsn.load(infile)
|
||||
print('LOADED : config.json')
|
||||
|
||||
except FileNotFoundError:
|
||||
with open('config.json', 'w') as outfile:
|
||||
jsn.dump({'client_id': 0, 'info_channel': 0, 'owner_id': 0, 'permissions': 126016,
|
||||
'playing': 'a game', 'prefix': [',', 'm,'], 'selfbot': False, 'token': 'str'}, outfile, indent=4, sort_keys=True)
|
||||
print('FILE NOT FOUND : config.json created with abstract values. Restart run.py with correct values')
|
||||
|
||||
|
||||
def setdefault(filename, default=None, json=False):
|
||||
if json:
|
||||
try:
|
||||
with open(filename, 'r') as infile:
|
||||
print(f'LOADED : {filename}')
|
||||
return jsn.load(infile)
|
||||
|
||||
except FileNotFoundError:
|
||||
with open(filename, 'w+') as iofile:
|
||||
print(f'FILE NOT FOUND : {filename} created and loaded with default values')
|
||||
jsn.dump(default, iofile)
|
||||
iofile.seek(0)
|
||||
return jsn.load(iofile)
|
||||
else:
|
||||
try:
|
||||
with open(filename, 'rb') as infile:
|
||||
print(f'LOADED : {filename}')
|
||||
return pkl.load(infile)
|
||||
|
||||
except FileNotFoundError:
|
||||
with open(filename, 'wb+') as iofile:
|
||||
print(f'FILE NOT FOUND : {filename} created and loaded with default values')
|
||||
pkl.dump(default, iofile)
|
||||
iofile.seek(0)
|
||||
return pkl.load(iofile)
|
||||
|
||||
|
||||
def load(filename, *, json=False):
|
||||
if not json:
|
||||
with open(filename, 'rb') as infile:
|
||||
return pkl.load(infile)
|
||||
else:
|
||||
with open(filename) as infile:
|
||||
return jsn.load(infile)
|
||||
|
||||
|
||||
def dump(obj, filename, *, json=False):
|
||||
if not json:
|
||||
with open(filename, 'wb') as outfile:
|
||||
pkl.dump(obj, outfile)
|
||||
else:
|
||||
with open(filename, 'w') as outfile:
|
||||
jsn.dump(obj, outfile, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
settings = setdefault('misc/settings.pkl', default={'del_ctx': [], 'del_resp': [], 'prefixes': {}})
|
||||
tasks = setdefault('cogs/tasks.pkl', default={'auto_del': [], 'auto_hrt': [], 'auto_rev': [], 'periodic_gpm': []})
|
||||
temp = setdefault('temp/temp.pkl', default={'startup': ()})
|
||||
secrets = setdefault('secrets.json', default={'client_secrets': {'client_id': '', 'client_secret': ''}}, json=True)
|
||||
|
||||
RATE_LIMIT = 2.2
|
||||
color = d.Color(0x1A1A1A)
|
||||
session = aiohttp.ClientSession()
|
||||
last_commands = {}
|
||||
|
||||
|
||||
async def fetch(url, *, params={}, json=False, response=False):
|
||||
async with session.get(url, params=params, headers={'User-Agent': 'Myned/Modufur'}) as r:
|
||||
if response:
|
||||
return r
|
||||
elif json:
|
||||
return await r.json()
|
||||
return await r.read()
|
||||
|
||||
|
||||
# async def clear(obj, interval=10 * 60, replace=None):
|
||||
# if replace is None:
|
||||
# if type(obj) is list:
|
||||
# replace = []
|
||||
# elif type(obj) is dict:
|
||||
# replace = {}
|
||||
# elif type(obj) is int:
|
||||
# replace = 0
|
||||
# elif type(obj) is str:
|
||||
# replace = ''
|
||||
#
|
||||
# while True:
|
||||
# obj = replace
|
||||
# asyncio.sleep(interval)
|
||||
|
||||
|
||||
def close(loop):
|
||||
if session:
|
||||
session.close()
|
||||
|
||||
loop.stop()
|
||||
pending = asyncio.Task.all_tasks()
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
# with suppress(asyncio.CancelledError):
|
||||
# loop.run_until_complete(task)
|
||||
# loop.close()
|
||||
|
||||
print('Finished cancelling tasks.')
|
||||
|
||||
|
||||
def generate_embed(ctx, *, title=d.Embed.Empty, kind='rich', description=d.Embed.Empty, url=d.Embed.Empty, timestamp=d.Embed.Empty, colour=color, footer={}, image=d.Embed.Empty, thumbnail=d.Embed.Empty, author={}, fields=[]):
|
||||
embed = d.Embed(title=title, type=kind, description=description, url=url, timestamp=timestamp, colour=colour if isinstance(ctx.channel, d.TextChannel) else color)
|
||||
|
||||
if footer:
|
||||
embed.set_footer(text=footer.get('text', d.Embed.Empty), icon_url=footer.get('icon_url', d.Embed.Empty))
|
||||
if image:
|
||||
embed.set_image(url=image)
|
||||
if thumbnail:
|
||||
embed.set_thumbnail(url=thumbnail)
|
||||
if author:
|
||||
embed.set_author(name=author.get('name', d.Embed.Empty), url=author.get('url', d.Embed.Empty), icon_url=author.get('icon_url', d.Embed.Empty))
|
||||
for field in fields:
|
||||
embed.add_field(name=field.get('name', d.Embed.Empty), value=field.get('value', d.Embed.Empty), inline=field.get('inline', True))
|
||||
|
||||
return embed
|
||||
|
||||
def get_kwargs(ctx, args, *, limit=False):
|
||||
destination = ctx
|
||||
remaining = list(args[:])
|
||||
rm = False
|
||||
lim = 1
|
||||
|
||||
for flag in ('-d', '-dm'):
|
||||
if flag in remaining:
|
||||
destination = ctx.author
|
||||
|
||||
remaining.remove(flag)
|
||||
|
||||
for flag in ('-r', '-rm', '-remove', '-re', '-repl', '-replace'):
|
||||
if flag in remaining and ctx.author.permissions_in(ctx.channel).manage_messages:
|
||||
rm = True
|
||||
|
||||
remaining.remove(flag)
|
||||
|
||||
if limit:
|
||||
for arg in remaining:
|
||||
if arg.isdigit():
|
||||
if 1 <= int(arg) <= limit:
|
||||
lim = int(arg)
|
||||
remaining.remove(arg)
|
||||
break
|
||||
else:
|
||||
raise exc.BoundsError(arg)
|
||||
|
||||
return {'destination': destination, 'remaining': remaining, 'remove': rm, 'limit': lim}
|
||||
|
||||
|
||||
def get_aspectratio(a, b):
|
||||
divisor = gcd(a, b)
|
||||
return f'{int(a / divisor)}:{int(b / divisor)}'
|
||||
|
||||
|
||||
def ci(pos, n):
|
||||
z = 1.96
|
||||
phat = float(pos) / n
|
||||
|
||||
return (phat + z*z/(2*n) - z * math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
|
||||
|
|
Loading…
Reference in a new issue