cacassetout #1
4
.gitignore
vendored
4
.gitignore
vendored
@ -73,4 +73,6 @@ data/*
|
||||
|
||||
.env
|
||||
|
||||
_build
|
||||
/doc/build
|
||||
/src/datas/
|
||||
/src/doctest_config.toml
|
||||
|
12
Pipfile
12
Pipfile
@ -5,19 +5,13 @@ name = "pypi"
|
||||
|
||||
[packages]
|
||||
packaging = "*"
|
||||
aiohttp = "*"
|
||||
aiofiles = "*"
|
||||
lupa = "*"
|
||||
aiofile = "*"
|
||||
toml = "*"
|
||||
"discord.py" = {version = "*",extras = ["voice",]}
|
||||
matplotlib = "*"
|
||||
humanize = "*"
|
||||
|
||||
[dev-packages]
|
||||
pytest = "*"
|
||||
sphinx = "*"
|
||||
sphinx-rtd-theme = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
||||
python_version = "3.8"
|
||||
|
483
Pipfile.lock
generated
483
Pipfile.lock
generated
@ -1,11 +1,11 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "83d48f54ad14a40d4bc3b81715c5ef5bb4e604b11a7da3e9ad4db62826ff37ae"
|
||||
"sha256": "56274993c51b422f6d178e8a21bbf669b8b508facb513d6c63f3373ebf707863"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.7"
|
||||
"python_version": "3.8"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
@ -16,27 +16,6 @@
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"aiofile": {
|
||||
"hashes": [
|
||||
"sha256:229078abbaab87adfcaad0fa7766b9b8251d42d0242deac6166da433b027ef1f",
|
||||
"sha256:312d50ed7e646a40ab2a5457fdf382870aca926f956921ab8c7ab72c3922f372",
|
||||
"sha256:8c50fcb42ee2bad2ae811edb972724e7f6bf3b0a6565a498f2432862b548b92d",
|
||||
"sha256:a9a457654e561c396b88f70a0d5fa00e40a337853aa180bc805d9d5efb82317c",
|
||||
"sha256:cef9e7bdf93db6a4c7ffe9ef0c354e2887695ec2a3a9dda8ed285005ec835616",
|
||||
"sha256:d1da2fc9aa7509d29ea09617bf533bd1045f79cfdfb10ee83da90ba2212720a2",
|
||||
"sha256:e43cb5e3181a8dfb73afbb4749b024e9a35a52e60ecf97d1d3db2731212cb0a0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5.2"
|
||||
},
|
||||
"aiofiles": {
|
||||
"hashes": [
|
||||
"sha256:377fdf7815cc611870c59cbd07b68b180841d2a2b79812d8c218be02448c2acb",
|
||||
"sha256:98e6bcfd1b50f97db4980e182ddd509b7cc35909e903a8fe50d8849e02d815af"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.5.0"
|
||||
},
|
||||
"aiohttp": {
|
||||
"hashes": [
|
||||
"sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e",
|
||||
@ -52,16 +31,8 @@
|
||||
"sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59",
|
||||
"sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.6.2"
|
||||
},
|
||||
"alabaster": {
|
||||
"hashes": [
|
||||
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
|
||||
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
|
||||
],
|
||||
"version": "==0.7.12"
|
||||
},
|
||||
"async-timeout": {
|
||||
"hashes": [
|
||||
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
|
||||
@ -76,20 +47,6 @@
|
||||
],
|
||||
"version": "==19.3.0"
|
||||
},
|
||||
"babel": {
|
||||
"hashes": [
|
||||
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
|
||||
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
|
||||
],
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
|
||||
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
|
||||
],
|
||||
"version": "==2020.4.5.1"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff",
|
||||
@ -130,13 +87,6 @@
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"cycler": {
|
||||
"hashes": [
|
||||
"sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d",
|
||||
"sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"
|
||||
],
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"discord.py": {
|
||||
"extras": [
|
||||
"voice"
|
||||
@ -148,6 +98,184 @@
|
||||
"index": "pypi",
|
||||
"version": "==1.3.3"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
||||
],
|
||||
"version": "==2.9"
|
||||
},
|
||||
"multidict": {
|
||||
"hashes": [
|
||||
"sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1",
|
||||
"sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35",
|
||||
"sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928",
|
||||
"sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969",
|
||||
"sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e",
|
||||
"sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78",
|
||||
"sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1",
|
||||
"sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136",
|
||||
"sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8",
|
||||
"sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2",
|
||||
"sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e",
|
||||
"sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4",
|
||||
"sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5",
|
||||
"sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd",
|
||||
"sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab",
|
||||
"sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20",
|
||||
"sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3"
|
||||
],
|
||||
"version": "==4.7.5"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3",
|
||||
"sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==20.3"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"version": "==2.20"
|
||||
},
|
||||
"pynacl": {
|
||||
"hashes": [
|
||||
"sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255",
|
||||
"sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c",
|
||||
"sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e",
|
||||
"sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae",
|
||||
"sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621",
|
||||
"sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56",
|
||||
"sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39",
|
||||
"sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310",
|
||||
"sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1",
|
||||
"sha256:53126cd91356342dcae7e209f840212a58dcf1177ad52c1d938d428eebc9fee5",
|
||||
"sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a",
|
||||
"sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786",
|
||||
"sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b",
|
||||
"sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b",
|
||||
"sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f",
|
||||
"sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20",
|
||||
"sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415",
|
||||
"sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715",
|
||||
"sha256:bf459128feb543cfca16a95f8da31e2e65e4c5257d2f3dfa8c0c1031139c9c92",
|
||||
"sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1",
|
||||
"sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||
],
|
||||
"version": "==2.4.7"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
||||
],
|
||||
"version": "==1.14.0"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"websockets": {
|
||||
"hashes": [
|
||||
"sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5",
|
||||
"sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5",
|
||||
"sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308",
|
||||
"sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb",
|
||||
"sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a",
|
||||
"sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c",
|
||||
"sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170",
|
||||
"sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422",
|
||||
"sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8",
|
||||
"sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485",
|
||||
"sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f",
|
||||
"sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8",
|
||||
"sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc",
|
||||
"sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779",
|
||||
"sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989",
|
||||
"sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1",
|
||||
"sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092",
|
||||
"sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824",
|
||||
"sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d",
|
||||
"sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55",
|
||||
"sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36",
|
||||
"sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"
|
||||
],
|
||||
"version": "==8.1"
|
||||
},
|
||||
"yarl": {
|
||||
"hashes": [
|
||||
"sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce",
|
||||
"sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6",
|
||||
"sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce",
|
||||
"sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae",
|
||||
"sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d",
|
||||
"sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f",
|
||||
"sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b",
|
||||
"sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b",
|
||||
"sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb",
|
||||
"sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462",
|
||||
"sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea",
|
||||
"sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70",
|
||||
"sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1",
|
||||
"sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a",
|
||||
"sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b",
|
||||
"sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080",
|
||||
"sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2"
|
||||
],
|
||||
"version": "==1.4.2"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"alabaster": {
|
||||
"hashes": [
|
||||
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
|
||||
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
|
||||
],
|
||||
"version": "==0.7.12"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
|
||||
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
|
||||
],
|
||||
"version": "==19.3.0"
|
||||
},
|
||||
"babel": {
|
||||
"hashes": [
|
||||
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
|
||||
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
|
||||
],
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
|
||||
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
|
||||
],
|
||||
"version": "==2020.4.5.1"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
|
||||
@ -155,14 +283,6 @@
|
||||
],
|
||||
"version": "==0.16"
|
||||
},
|
||||
"humanize": {
|
||||
"hashes": [
|
||||
"sha256:98b7ac9d1a70ad62175c8e0dd44beebbd92418727fc4e214468dfb2baa8ebfb5",
|
||||
"sha256:bc2a1ff065977011de2bc36197a4b14730c54bfc46ab12a153376684573a2dab"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.3.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||
@ -177,14 +297,6 @@
|
||||
],
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
|
||||
"sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
|
||||
],
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==1.6.0"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
|
||||
@ -192,58 +304,6 @@
|
||||
],
|
||||
"version": "==2.11.2"
|
||||
},
|
||||
"kiwisolver": {
|
||||
"hashes": [
|
||||
"sha256:03662cbd3e6729f341a97dd2690b271e51a67a68322affab12a5b011344b973c",
|
||||
"sha256:18d749f3e56c0480dccd1714230da0f328e6e4accf188dd4e6884bdd06bf02dd",
|
||||
"sha256:247800260cd38160c362d211dcaf4ed0f7816afb5efe56544748b21d6ad6d17f",
|
||||
"sha256:443c2320520eda0a5b930b2725b26f6175ca4453c61f739fef7a5847bd262f74",
|
||||
"sha256:4eadb361baf3069f278b055e3bb53fa189cea2fd02cb2c353b7a99ebb4477ef1",
|
||||
"sha256:556da0a5f60f6486ec4969abbc1dd83cf9b5c2deadc8288508e55c0f5f87d29c",
|
||||
"sha256:603162139684ee56bcd57acc74035fceed7dd8d732f38c0959c8bd157f913fec",
|
||||
"sha256:60a78858580761fe611d22127868f3dc9f98871e6fdf0a15cc4203ed9ba6179b",
|
||||
"sha256:7cc095a4661bdd8a5742aaf7c10ea9fac142d76ff1770a0f84394038126d8fc7",
|
||||
"sha256:c31bc3c8e903d60a1ea31a754c72559398d91b5929fcb329b1c3a3d3f6e72113",
|
||||
"sha256:c955791d80e464da3b471ab41eb65cf5a40c15ce9b001fdc5bbc241170de58ec",
|
||||
"sha256:d069ef4b20b1e6b19f790d00097a5d5d2c50871b66d10075dab78938dc2ee2cf",
|
||||
"sha256:d52b989dc23cdaa92582ceb4af8d5bcc94d74b2c3e64cd6785558ec6a879793e",
|
||||
"sha256:e586b28354d7b6584d8973656a7954b1c69c93f708c0c07b77884f91640b7657",
|
||||
"sha256:efcf3397ae1e3c3a4a0a0636542bcad5adad3b1dd3e8e629d0b6e201347176c8",
|
||||
"sha256:fccefc0d36a38c57b7bd233a9b485e2f1eb71903ca7ad7adacad6c28a56d62d2"
|
||||
],
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"lupa": {
|
||||
"hashes": [
|
||||
"sha256:09d6c45eb3b9407588c5a168e3371b629e75c5822050e9feff393601709bd0d7",
|
||||
"sha256:162f6793b2ad40d25710b9998bce2eeb3938efbb4dbad49fb8c5082d214237b3",
|
||||
"sha256:2551ae82ea0f90383fb153ecd29a1a166e2552e10b7a712ff047cad88062ad37",
|
||||
"sha256:42285855c022b36ed3f0c5d19d0ef27b1648e0683838cddaf9191acad4d6616c",
|
||||
"sha256:42fcd8f7b33b84abce90c57aaeb80d9a2ba3c3fdb4cde2fac1c8f9e4eb00d581",
|
||||
"sha256:49afbeaf90c758512d3c0dea48ac0ecfa460974690cf1af58b95845e6b607c4b",
|
||||
"sha256:4badf4180f8fd28e032e8716422b7a0117879569e694b5e2e803a7e39fa85213",
|
||||
"sha256:517b96b23b4ce19feb54ee93d8c3b94f601a3d46cd1d570ecc5137fc7b9cb68c",
|
||||
"sha256:632e7a101c288e05b823c2bae71ac69e0253e7f4120bc39b5dc1fcaf5daba0fb",
|
||||
"sha256:6d65bdc251cd12b85487a1790ca1b282288be84555fe11fbe8b4357ae64708f5",
|
||||
"sha256:7619fbd85d9ece1d48fb72bb7389e98d878621d2da0b7622c99066671f294b65",
|
||||
"sha256:8434fdda16d101c458570d21baf9cd064304b515ed4ef9569949222ba04c3e37",
|
||||
"sha256:9823322e60b0d9695754e28f5a17323d111d6951933e958cfe72df9523a39e94",
|
||||
"sha256:9ee2aa3e1e852a2917c5869e8ab69d725407a218d14c4c0c98f4b04b3b2a73a7",
|
||||
"sha256:a3e11d806ca02cf72e490ec1974f8b96a14a1091895c9dccebe0b8d52dd82e8e",
|
||||
"sha256:a690b0bafb7e50dd8ba14a06065059b11f5c8e5961564d5d45de2d9b4a9972b1",
|
||||
"sha256:a7d7761b007fbf8b524291ac42bccc32b072102e7f7e547783a5a5ded66a0c39",
|
||||
"sha256:abb357c35ad1c1b78b140c8cf1fd678bcaa04bab275c6d55e47a07717138e551",
|
||||
"sha256:ac7585125af7d7214e1f9dbdda965d7455c5065f71be20374c7900e01c74c05f",
|
||||
"sha256:acaecd88ce6b708fbaf20b76b4d35ecb2817159f8a939b0a73d2aa840dfef850",
|
||||
"sha256:ba879849832b87c18dbc471bffc62ff3393b2034a3b103348d620646575f448a",
|
||||
"sha256:c57cda6ba3dc55ddd8b6c566c4f315d6152307aee23f212aa06c5e653cde4f13",
|
||||
"sha256:d3cf15d0c1126373535452bdeb71b016fe970d7e5ee2bc0381df7bd35f99c820",
|
||||
"sha256:d497f4727060a1daf8603e86cb731f587c38ab9a3451cd3c9c70f27859cbd3bd",
|
||||
"sha256:fe1db400b471a0854fe364b63d7836973ee0d897a76628340d1721b6b4b89ddc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.9"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
||||
@ -282,26 +342,6 @@
|
||||
],
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"matplotlib": {
|
||||
"hashes": [
|
||||
"sha256:2466d4dddeb0f5666fd1e6736cc5287a4f9f7ae6c1a9e0779deff798b28e1d35",
|
||||
"sha256:282b3fc8023c4365bad924d1bb442ddc565c2d1635f210b700722776da466ca3",
|
||||
"sha256:4bb50ee4755271a2017b070984bcb788d483a8ce3132fab68393d1555b62d4ba",
|
||||
"sha256:56d3147714da5c7ac4bc452d041e70e0e0b07c763f604110bd4e2527f320b86d",
|
||||
"sha256:7a9baefad265907c6f0b037c8c35a10cf437f7708c27415a5513cf09ac6d6ddd",
|
||||
"sha256:aae7d107dc37b4bb72dcc45f70394e6df2e5e92ac4079761aacd0e2ad1d3b1f7",
|
||||
"sha256:af14e77829c5b5d5be11858d042d6f2459878f8e296228c7ea13ec1fd308eb68",
|
||||
"sha256:c1cf735970b7cd424502719b44288b21089863aaaab099f55e0283a721aaf781",
|
||||
"sha256:ce378047902b7a05546b6485b14df77b2ff207a0054e60c10b5680132090c8ee",
|
||||
"sha256:d35891a86a4388b6965c2d527b9a9f9e657d9e110b0575ca8a24ba0d4e34b8fc",
|
||||
"sha256:e06304686209331f99640642dee08781a9d55c6e32abb45ed54f021f46ccae47",
|
||||
"sha256:e20ba7fb37d4647ac38f3c6d8672dd8b62451ee16173a0711b37ba0ce42bf37d",
|
||||
"sha256:f4412241e32d0f8d3713b68d3ca6430190a5e8a7c070f1c07d7833d8c5264398",
|
||||
"sha256:ffe2f9cdcea1086fc414e82f42271ecf1976700b8edd16ca9d376189c6d93aee"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.1"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c",
|
||||
@ -309,54 +349,6 @@
|
||||
],
|
||||
"version": "==8.2.0"
|
||||
},
|
||||
"multidict": {
|
||||
"hashes": [
|
||||
"sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1",
|
||||
"sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35",
|
||||
"sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928",
|
||||
"sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969",
|
||||
"sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e",
|
||||
"sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78",
|
||||
"sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1",
|
||||
"sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136",
|
||||
"sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8",
|
||||
"sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2",
|
||||
"sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e",
|
||||
"sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4",
|
||||
"sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5",
|
||||
"sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd",
|
||||
"sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab",
|
||||
"sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20",
|
||||
"sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3"
|
||||
],
|
||||
"version": "==4.7.5"
|
||||
},
|
||||
"numpy": {
|
||||
"hashes": [
|
||||
"sha256:0aa2b318cf81eb1693fcfcbb8007e95e231d7e1aa24288137f3b19905736c3ee",
|
||||
"sha256:163c78c04f47f26ca1b21068cea25ed7c5ecafe5f5ab2ea4895656a750582b56",
|
||||
"sha256:1e37626bcb8895c4b3873fcfd54e9bfc5ffec8d0f525651d6985fcc5c6b6003c",
|
||||
"sha256:264fd15590b3f02a1fbc095e7e1f37cdac698ff3829e12ffdcffdce3772f9d44",
|
||||
"sha256:3d9e1554cd9b5999070c467b18e5ae3ebd7369f02706a8850816f576a954295f",
|
||||
"sha256:40c24960cd5cec55222963f255858a1c47c6fa50a65a5b03fd7de75e3700eaaa",
|
||||
"sha256:46f404314dbec78cb342904f9596f25f9b16e7cf304030f1339e553c8e77f51c",
|
||||
"sha256:4847f0c993298b82fad809ea2916d857d0073dc17b0510fbbced663b3265929d",
|
||||
"sha256:48e15612a8357393d176638c8f68a19273676877caea983f8baf188bad430379",
|
||||
"sha256:6725d2797c65598778409aba8cd67077bb089d5b7d3d87c2719b206dc84ec05e",
|
||||
"sha256:99f0ba97e369f02a21bb95faa3a0de55991fd5f0ece2e30a9e2eaebeac238921",
|
||||
"sha256:a41f303b3f9157a31ce7203e3ca757a0c40c96669e72d9b6ee1bce8507638970",
|
||||
"sha256:a4305564e93f5c4584f6758149fd446df39fd1e0a8c89ca0deb3cce56106a027",
|
||||
"sha256:a551d8cc267c634774830086da42e4ba157fa41dd3b93982bc9501b284b0c689",
|
||||
"sha256:a6bc9432c2640b008d5f29bad737714eb3e14bb8854878eacf3d7955c4e91c36",
|
||||
"sha256:c60175d011a2e551a2f74c84e21e7c982489b96b6a5e4b030ecdeacf2914da68",
|
||||
"sha256:e46e2384209c91996d5ec16744234d1c906ab79a701ce1a26155c9ec890b8dc8",
|
||||
"sha256:e607b8cdc2ae5d5a63cd1bec30a15b5ed583ac6a39f04b7ba0f03fcfbf29c05b",
|
||||
"sha256:e94a39d5c40fffe7696009dbd11bc14a349b377e03a384ed011e03d698787dd3",
|
||||
"sha256:eb2286249ebfe8fcb5b425e5ec77e4736d53ee56d3ad296f8947f67150f495e3",
|
||||
"sha256:fdee7540d12519865b423af411bd60ddb513d2eb2cd921149b732854995bbf8b"
|
||||
],
|
||||
"version": "==1.18.3"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3",
|
||||
@ -379,13 +371,6 @@
|
||||
],
|
||||
"version": "==1.8.1"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"version": "==2.20"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
|
||||
@ -393,32 +378,6 @@
|
||||
],
|
||||
"version": "==2.6.1"
|
||||
},
|
||||
"pynacl": {
|
||||
"hashes": [
|
||||
"sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255",
|
||||
"sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c",
|
||||
"sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e",
|
||||
"sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae",
|
||||
"sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621",
|
||||
"sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56",
|
||||
"sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39",
|
||||
"sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310",
|
||||
"sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1",
|
||||
"sha256:53126cd91356342dcae7e209f840212a58dcf1177ad52c1d938d428eebc9fee5",
|
||||
"sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a",
|
||||
"sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786",
|
||||
"sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b",
|
||||
"sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b",
|
||||
"sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f",
|
||||
"sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20",
|
||||
"sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415",
|
||||
"sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715",
|
||||
"sha256:bf459128feb543cfca16a95f8da31e2e65e4c5257d2f3dfa8c0c1031139c9c92",
|
||||
"sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1",
|
||||
"sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
@ -434,13 +393,6 @@
|
||||
"index": "pypi",
|
||||
"version": "==5.4.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||
],
|
||||
"version": "==2.8.1"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
|
||||
@ -477,14 +429,6 @@
|
||||
"index": "pypi",
|
||||
"version": "==3.0.2"
|
||||
},
|
||||
"sphinx-autodoc-typehints": {
|
||||
"hashes": [
|
||||
"sha256:27c9e6ef4f4451766ab8d08b2d8520933b97beb21c913f3df9ab2e59b56e6c6c",
|
||||
"sha256:a6b3180167479aca2c4d1ed3b5cb044a70a76cccd6b38662d39288ebd9f0dff0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.10.3"
|
||||
},
|
||||
"sphinx-rtd-theme": {
|
||||
"hashes": [
|
||||
"sha256:00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4",
|
||||
@ -535,14 +479,6 @@
|
||||
],
|
||||
"version": "==1.1.4"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
|
||||
@ -556,63 +492,6 @@
|
||||
"sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"
|
||||
],
|
||||
"version": "==0.1.9"
|
||||
},
|
||||
"websockets": {
|
||||
"hashes": [
|
||||
"sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5",
|
||||
"sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5",
|
||||
"sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308",
|
||||
"sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb",
|
||||
"sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a",
|
||||
"sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c",
|
||||
"sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170",
|
||||
"sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422",
|
||||
"sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8",
|
||||
"sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485",
|
||||
"sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f",
|
||||
"sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8",
|
||||
"sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc",
|
||||
"sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779",
|
||||
"sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989",
|
||||
"sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1",
|
||||
"sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092",
|
||||
"sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824",
|
||||
"sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d",
|
||||
"sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55",
|
||||
"sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36",
|
||||
"sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"
|
||||
],
|
||||
"version": "==8.1"
|
||||
},
|
||||
"yarl": {
|
||||
"hashes": [
|
||||
"sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce",
|
||||
"sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6",
|
||||
"sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce",
|
||||
"sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae",
|
||||
"sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d",
|
||||
"sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f",
|
||||
"sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b",
|
||||
"sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b",
|
||||
"sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb",
|
||||
"sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462",
|
||||
"sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea",
|
||||
"sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70",
|
||||
"sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1",
|
||||
"sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a",
|
||||
"sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b",
|
||||
"sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080",
|
||||
"sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2"
|
||||
],
|
||||
"version": "==1.4.2"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
|
||||
"sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
|
||||
],
|
||||
"version": "==3.1.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
from config.base import Config
|
||||
|
||||
__all__ = ["Config"]
|
@ -1,36 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from main import LBI
|
||||
|
||||
|
||||
class Channel(BaseType):
|
||||
client: LBI
|
||||
|
||||
def __init__(self, client):
|
||||
self.value = None
|
||||
self.client = client
|
||||
|
||||
def check_value(self, value):
|
||||
return True
|
||||
|
||||
def set(self, value):
|
||||
if self.check_value(value):
|
||||
self.value = value
|
||||
return
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def to_save(self):
|
||||
return self.value
|
||||
|
||||
def load(self, value):
|
||||
if self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
self.value = value
|
@ -1,36 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from main import LBI
|
||||
|
||||
|
||||
class Role(BaseType):
|
||||
client: LBI
|
||||
|
||||
def __init__(self, client):
|
||||
self.value = None
|
||||
self.client = client
|
||||
|
||||
def check_value(self, value):
|
||||
return True
|
||||
|
||||
def set(self, value):
|
||||
if self.check_value(value):
|
||||
self.value = value
|
||||
return
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def to_save(self):
|
||||
return self.value
|
||||
|
||||
def load(self, value):
|
||||
if self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
self.value = value
|
@ -1,36 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from main import LBI
|
||||
|
||||
class User(BaseType):
|
||||
|
||||
client: LBI
|
||||
|
||||
def __init__(self, client):
|
||||
self.value = None
|
||||
self.client = client
|
||||
|
||||
def check_value(self, value):
|
||||
return True
|
||||
|
||||
def set(self, value):
|
||||
if self.check_value(value):
|
||||
self.value = value
|
||||
return
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def to_save(self):
|
||||
return self.value
|
||||
|
||||
def load(self, value):
|
||||
if self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
self.value = value
|
@ -13,7 +13,7 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../'))
|
||||
sys.path.insert(0, os.path.abspath('../../src'))
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
28
errors.py
28
errors.py
@ -1,28 +0,0 @@
|
||||
class LBIException(Exception):
|
||||
"""
|
||||
Base exception class for LBI
|
||||
|
||||
All other exceptions are subclasses
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ModuleException(LBIException):
|
||||
"""
|
||||
Base exception class for all module errors
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ModuleNotInstalled(ModuleException):
|
||||
"""
|
||||
Raised when a module is not found in module directory
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class IncompatibleModule(ModuleException):
|
||||
"""
|
||||
Raised when a module is not compatible with bot version
|
||||
"""
|
||||
pass
|
499
main.py
499
main.py
@ -1,499 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import importlib
|
||||
import json
|
||||
import locale
|
||||
import logging
|
||||
import logging.config
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
import discord
|
||||
import humanize
|
||||
from packaging.version import Version
|
||||
|
||||
from config import Config, config_types
|
||||
from config.config_types import factory
|
||||
from errors import IncompatibleModule
|
||||
from modules.base import base_supported_type
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
|
||||
class Module:
|
||||
name: str
|
||||
|
||||
def __init__(self, name: str):
|
||||
"""
|
||||
Init module
|
||||
|
||||
:param name: Module name
|
||||
:type name: str
|
||||
"""
|
||||
self.name = name
|
||||
MODULES.update({self.name: self})
|
||||
|
||||
@property
|
||||
def type(self) -> str:
|
||||
"""
|
||||
Return module type. It can be python or lua
|
||||
|
||||
:return: Module type
|
||||
:rtype: str
|
||||
"""
|
||||
if not os.path.exists(os.path.join("modules", self.name, "version.json")):
|
||||
return ""
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
return versions["type"]
|
||||
|
||||
@property
|
||||
def exists(self) -> bool:
|
||||
"""
|
||||
Check if module exists
|
||||
|
||||
:return: True if module is present in modules folders
|
||||
:rtype: bool
|
||||
"""
|
||||
if not os.path.isdir(os.path.join("modules", self.name)):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def complete(self) -> bool:
|
||||
"""
|
||||
Check if module is complete
|
||||
|
||||
:return: True if module is compatible
|
||||
:rtype: Boolean
|
||||
"""
|
||||
# Check if version.json exists
|
||||
if not os.path.exists(os.path.join("modules", self.name, "version.json")):
|
||||
return False
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
if "version" not in versions.keys():
|
||||
return False
|
||||
if "dependencies" not in versions.keys():
|
||||
return False
|
||||
if "bot_version" not in versions.keys():
|
||||
return False
|
||||
if "type" not in versions.keys():
|
||||
return False
|
||||
if versions["type"] not in base_supported_type:
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def version(self) -> Version:
|
||||
"""
|
||||
Returns module version
|
||||
|
||||
:return: current module version
|
||||
:rtype: Version
|
||||
"""
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
return Version(versions["version"])
|
||||
|
||||
@property
|
||||
def bot_version(self) -> dict:
|
||||
"""
|
||||
returns the min and max version of the bot that is compatible with the module
|
||||
|
||||
:return: Min and max version for bot
|
||||
:rtype: dict
|
||||
:raises IncompatibleModule: If bot_version is not properly formated (there must be min and max keys)
|
||||
"""
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
try:
|
||||
return {"min": Version(versions["bot_version"]["min"]),
|
||||
"max": Version(versions["bot_version"]["max"])}
|
||||
except KeyError:
|
||||
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not "
|
||||
f"contain bot_version.max or bot_version.min item)")
|
||||
|
||||
@property
|
||||
def dependencies(self) -> dict:
|
||||
"""
|
||||
return list of dependencies version
|
||||
|
||||
:raise IncompatibleModule: If bot_version is not properly formated (there must be min and max keys for each dependencies)
|
||||
:return: list of dependencies version
|
||||
:rtype: dict
|
||||
"""
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
try:
|
||||
deps = {}
|
||||
for name, dep in versions["dependencies"].items():
|
||||
dep_ver = {"min": Version(dep["min"]),
|
||||
"max": Version(dep["max"])}
|
||||
deps.update({name: dep_ver})
|
||||
return deps
|
||||
except KeyError:
|
||||
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not "
|
||||
f"contain dependencies.modulename.max or dependencies.modulename.min item)")
|
||||
|
||||
@property
|
||||
def compatible(self) -> bool:
|
||||
"""
|
||||
Check if module is compatible with current installation
|
||||
|
||||
:return: True if all dependencies are okays
|
||||
:rtype: bool
|
||||
"""
|
||||
# Check bot version
|
||||
bot_ver = Version(__version__)
|
||||
if bot_ver < self.bot_version["min"]:
|
||||
return False
|
||||
if bot_ver > self.bot_version["max"]:
|
||||
return False
|
||||
for name, dep in self.dependencies.items():
|
||||
if name not in MODULES.keys():
|
||||
Module(name)
|
||||
if MODULES[name].version < dep["min"]:
|
||||
return False
|
||||
if MODULES[name].version > dep["max"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
MODULES: Dict[str, Module] = {}
|
||||
|
||||
|
||||
def setup_logging(default_path='config/log_config.json', default_level=logging.INFO, env_key='LBI_LOG_CONFIG'):
|
||||
"""Setup logging configuration
|
||||
"""
|
||||
path = default_path
|
||||
value = os.getenv(env_key, None)
|
||||
if value:
|
||||
path = value
|
||||
if os.path.exists(path):
|
||||
with open(path, 'rt') as f:
|
||||
config = json.load(f)
|
||||
logging.config.dictConfig(config)
|
||||
else:
|
||||
logging.basicConfig(level=default_level)
|
||||
|
||||
|
||||
def modules_edit(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.reloading:
|
||||
return func(self, *args, **kwargs)
|
||||
else:
|
||||
self.reloading = True
|
||||
a = func(self, *args, **kwargs)
|
||||
self.reloading = False
|
||||
return a
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def event(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.reloading:
|
||||
return lambda: None
|
||||
else:
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
"""def async_event(func):
|
||||
async def wrapper(self, *args, **kwargs):
|
||||
if self.reloading:
|
||||
return lambda: None
|
||||
else:
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper"""
|
||||
|
||||
setup_logging()
|
||||
|
||||
log_discord = logging.getLogger('discord_types')
|
||||
log_LBI = logging.getLogger('LBI')
|
||||
log_communication = logging.getLogger('communication')
|
||||
|
||||
|
||||
def load_modules_info():
|
||||
for mod in os.listdir("modules"):
|
||||
Module(mod)
|
||||
|
||||
|
||||
class LBI(discord.Client):
|
||||
by_id: ClientById
|
||||
base_path = "data"
|
||||
debug = log_LBI.debug
|
||||
info = log_LBI.info
|
||||
warning = log_LBI.warning
|
||||
warn = warning
|
||||
error = log_LBI.error
|
||||
critical = log_LBI.critical
|
||||
|
||||
def __init__(self, config: Config = None, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if config is None:
|
||||
config = Config(path="data/config.toml", client=self)
|
||||
self.reloading = False
|
||||
self.by_id = ClientById(self)
|
||||
self.ready = False
|
||||
# Content: {"module_name": {"module": imported module, "class": initialized class}}
|
||||
self.modules = {}
|
||||
|
||||
self.config = config
|
||||
self.config.register("modules", factory(config_types.List, factory(config_types.Str)))
|
||||
self.config.register("prefix", factory(config_types.Str))
|
||||
self.config.register("admin_roles", factory(config_types.List, factory(config_types.discord_types.Role, self)))
|
||||
self.config.register("admin_users", factory(config_types.List, factory(config_types.discord_types.User, self)))
|
||||
self.config.register("main_guild", factory(config_types.discord_types.Guild, self))
|
||||
self.config.register("locale", factory(config_types.Str))
|
||||
|
||||
self.config.set({
|
||||
"modules": ["modules", "errors"],
|
||||
"prefix": "%",
|
||||
"admin_roles": [],
|
||||
"admin_users": [],
|
||||
"main_guild": None,
|
||||
"locale": "fr_FR.UTF8",
|
||||
})
|
||||
|
||||
locale.setlocale(locale.LC_TIME, self.config['locale'])
|
||||
humanize.i18n.activate(self.config['locale'])
|
||||
self.load_modules()
|
||||
|
||||
@modules_edit
|
||||
def load_modules(self):
|
||||
self.info("Starts to load modules...")
|
||||
e = {}
|
||||
for module in self.config["modules"]:
|
||||
e.update({module: self.load_module(module)})
|
||||
self.info("Finished to load all modules.")
|
||||
return e
|
||||
|
||||
@modules_edit
|
||||
def load_module(self, module):
|
||||
"""
|
||||
|
||||
Status codes:
|
||||
- 0: Module loaded
|
||||
- 1: Module not in modules folder
|
||||
- 2: Module incomplete
|
||||
- 3: Module incompatible
|
||||
|
||||
:param module: Module name
|
||||
:return: Status code
|
||||
"""
|
||||
|
||||
# Check module compatibility
|
||||
load_modules_info()
|
||||
if not MODULES.get(module):
|
||||
return 1
|
||||
if not MODULES[module].exists:
|
||||
return 1
|
||||
if not MODULES[module].complete:
|
||||
return 2
|
||||
if not MODULES[module].compatible:
|
||||
return 3
|
||||
deps = MODULES[module].dependencies
|
||||
for dep in deps.keys():
|
||||
if dep not in self.modules.keys():
|
||||
if dep != "base":
|
||||
self.load_module(dep)
|
||||
if MODULES[module].type == "python":
|
||||
try:
|
||||
self.info("Start loading module {module}...".format(module=module))
|
||||
imported = importlib.import_module('modules.' + module)
|
||||
importlib.reload(imported)
|
||||
initialized_class = imported.MainClass(self)
|
||||
self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}})
|
||||
self.info("Module {module} successfully imported.".format(module=module))
|
||||
initialized_class.dispatch("load")
|
||||
|
||||
if module not in self.config["modules"]:
|
||||
self.config["modules"].append(module)
|
||||
self.config.save()
|
||||
except AttributeError as e:
|
||||
self.error("Module {module} doesn't have MainClass.".format(module=module))
|
||||
raise e
|
||||
return 0
|
||||
elif MODULES[module].type == "lua":
|
||||
self.info(f"Start loading module {module}...")
|
||||
imported = importlib.import_module('modules.base.BaseLua')
|
||||
importlib.reload(imported)
|
||||
initialized_class = imported.BaseClassLua(self, path=f"modules/{module}/main")
|
||||
self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}})
|
||||
self.info(f"Module {module} successfully imported.")
|
||||
initialized_class.dispatch("load")
|
||||
if module not in self.config["modules"]:
|
||||
self.config["modules"].append(module)
|
||||
self.config.save()
|
||||
return 0
|
||||
|
||||
@modules_edit
|
||||
def unload_module(self, module):
|
||||
self.info("Start unload module {module}...".format(module=module))
|
||||
try:
|
||||
if module in self.config["modules"]:
|
||||
self.config["modules"].remove(module)
|
||||
self.config.save()
|
||||
self.unload_all()
|
||||
self.load_modules()
|
||||
except KeyError as e:
|
||||
self.error("Module {module} not loaded.").format(module=module)
|
||||
return e
|
||||
|
||||
@modules_edit
|
||||
def reload(self):
|
||||
del self.modules
|
||||
self.load_modules()
|
||||
|
||||
@modules_edit
|
||||
def unload_all(self):
|
||||
del self.modules
|
||||
self.modules = {}
|
||||
|
||||
@event
|
||||
def dispatch(self, event, *args, **kwargs):
|
||||
# Dispatch to handle wait_* commands
|
||||
super().dispatch(event, *args, **kwargs)
|
||||
# Dispatch to modules
|
||||
for module in self.modules.values():
|
||||
module["initialized_class"].dispatch(event, *args, **kwargs)
|
||||
|
||||
async def on_error(self, event_method, *args, **kwargs):
|
||||
"""Function called when error happend"""
|
||||
# This event is special because it is call directly
|
||||
self.error(traceback.format_exc())
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_error(event_method, *args, **kwargs)
|
||||
|
||||
|
||||
class ClientById:
|
||||
client: LBI
|
||||
|
||||
def __init__(self, client_):
|
||||
self.client = client_
|
||||
|
||||
async def fetch_message(self, id_, *args, **kwargs):
|
||||
"""Find a message by id
|
||||
|
||||
:param id_: Id of message to find
|
||||
:type id_: int
|
||||
|
||||
:raises discord_types.NotFound: This exception is raised when a message is not found (or not accessible by bot)
|
||||
|
||||
:rtype: discord.Message
|
||||
:return: discord_types.Message instance if message is found.
|
||||
"""
|
||||
msg = None
|
||||
for channel in self.client.get_all_channels():
|
||||
try:
|
||||
return await channel.fetch_message(id_, *args, **kwargs)
|
||||
except discord.NotFound:
|
||||
continue
|
||||
if msg is None:
|
||||
raise discord.NotFound(None, "Message not found")
|
||||
|
||||
async def edit_message(self, id, *args, **kwargs):
|
||||
"""
|
||||
Edit message by id
|
||||
|
||||
:param id: Id of the message to edit
|
||||
:type id: int"""
|
||||
message = await self.fetch_message(id)
|
||||
return await message.edit(**kwargs)
|
||||
|
||||
async def remove_reaction(self, id_message, *args, **kwargs):
|
||||
"""Remove reaction from message by id
|
||||
|
||||
:param id_message: Id of message
|
||||
:type id_message: int"""
|
||||
message = await self.fetch_message(id_message)
|
||||
return await message.remove_reaction(*args, **kwargs)
|
||||
|
||||
async def send_message(self, id_, *args, **kwargs):
|
||||
"""Send message by channel id
|
||||
|
||||
:param id_: Id of channel where to send message
|
||||
:type id_: int"""
|
||||
channel = self.client.get_channel(id_)
|
||||
return channel.send(*args, **kwargs)
|
||||
|
||||
def get_role(self, id_=None, name=None, check=None, guilds=None):
|
||||
"""Get role by id or with custom check"""
|
||||
if guilds is None:
|
||||
guilds = self.client.guilds
|
||||
if id_ is not None:
|
||||
for guild in guilds:
|
||||
role = discord.utils.get(guild.roles, id=id_)
|
||||
if role:
|
||||
return role
|
||||
if name is not None:
|
||||
for guild in guilds:
|
||||
role = discord.utils.get(guild.roles, name=name)
|
||||
if role:
|
||||
return role
|
||||
if check is not None:
|
||||
role = None
|
||||
for guild in guilds:
|
||||
for role_ in guild.roles:
|
||||
if check(role_):
|
||||
role = role_
|
||||
break
|
||||
if role is not None:
|
||||
break
|
||||
return role
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
class Communication(asyncio.Protocol):
|
||||
debug = log_communication.debug
|
||||
info = log_communication.info
|
||||
warning = log_communication.warning
|
||||
error = log_communication.error
|
||||
critical = log_communication.critical
|
||||
name = "Communication"
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.transport = None
|
||||
|
||||
def connection_made(self, transport):
|
||||
print('%s: connection made' % self.name)
|
||||
self.transport = transport
|
||||
|
||||
def data_received(self, data):
|
||||
print('%s: data received: %r' % (self.name, data))
|
||||
|
||||
def eof_received(self):
|
||||
pass
|
||||
|
||||
def connection_lost(self, exc):
|
||||
print('%s: connection lost: %s' % (self.name, exc))
|
||||
|
||||
if __name__ == "__main__":
|
||||
client1 = LBI(max_messages=500000)
|
||||
communication = Communication(client1)
|
||||
|
||||
|
||||
async def start_bot():
|
||||
await client1.start(os.environ.get("DISCORD_TOKEN"))
|
||||
|
||||
|
||||
print(os.path.join("/tmp", os.path.dirname(os.path.realpath(__file__))) + ".sock")
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
t = loop.create_unix_server(Communication,
|
||||
path=os.path.join("/tmp", os.path.dirname(os.path.realpath(__file__)) + ".sock"))
|
||||
if not sys.platform == "win32":
|
||||
loop.run_until_complete(t)
|
||||
|
||||
loop.create_task(start_bot())
|
||||
loop.run_forever()
|
@ -1,56 +0,0 @@
|
||||
import datetime
|
||||
|
||||
import discord
|
||||
|
||||
import utils.emojis
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "Avalon"
|
||||
help = {
|
||||
"description": "Maître du jeu Avalon.",
|
||||
"commands": {
|
||||
"`{prefix}{command} join`": "",
|
||||
"`{prefix}{command} quit`": "",
|
||||
"`{prefix}{command} players list`": "",
|
||||
"`{prefix}{command} players kick (<user_id>/<@mention>)`": "",
|
||||
"`{prefix}{command} roles setup`": "",
|
||||
"`{prefix}{command} roles list`": "",
|
||||
}
|
||||
}
|
||||
help_active = True
|
||||
command_text = "perdu"
|
||||
color = 0xff6ba6
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.config.set({"spectate_channel": 0,
|
||||
"illustrations":{"merlin":"",
|
||||
"perceval":"",
|
||||
"gentil":"",
|
||||
"assassin":"",
|
||||
"mordred":"",
|
||||
"morgane":"",
|
||||
"oberon":"",
|
||||
"mechant":""},
|
||||
"couleurs":{"merlin":"",
|
||||
"perceval":0,
|
||||
"gentil":0,
|
||||
"assassin":0,
|
||||
"mordred":0,
|
||||
"morgane":0,
|
||||
"oberon":0,
|
||||
"mechant":0,
|
||||
"test":15},
|
||||
"test":{"merlin":"",
|
||||
"perceval":0,
|
||||
"gentil":0,
|
||||
"assassin":0,
|
||||
"mordred":0,
|
||||
"morgane":0,
|
||||
"oberon":0,
|
||||
"mechant":0,
|
||||
"test":15}
|
||||
})
|
||||
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,275 +0,0 @@
|
||||
"""Base class for module, never use directly !!!"""
|
||||
import asyncio
|
||||
import os
|
||||
from typing import List, Union, Optional
|
||||
|
||||
import discord
|
||||
|
||||
from config import Config, config_types
|
||||
from config.config_types import factory
|
||||
from storage import Objects
|
||||
from utils import emojis
|
||||
|
||||
|
||||
class BaseClass:
|
||||
"""Base class for all modules, Override it to make submodules"""
|
||||
name = ""
|
||||
help = {
|
||||
"description": "",
|
||||
"commands": {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
"""Initialize module class
|
||||
|
||||
Initialize module class, always call it to set self.client when you override it.
|
||||
|
||||
:param client: client instance
|
||||
:type client: LBI"""
|
||||
self.client = client
|
||||
self.objects = Objects(path=os.path.join("data", self.name.lower()))
|
||||
self.config = Config(path=os.path.join("data", self.name.lower(), "config.toml"))
|
||||
self.config.register("help_active", factory(config_types.Bool))
|
||||
self.config.register("color", factory(config_types.Color))
|
||||
self.config.register("auth_everyone", factory(config_types.Bool))
|
||||
self.config.register("authorized_roles",
|
||||
factory(config_types.List, factory(config_types.discord_types.Role, client)))
|
||||
self.config.register("authorized_users",
|
||||
factory(config_types.List, factory(config_types.discord_types.User, client)))
|
||||
self.config.register("command_text", factory(config_types.Str))
|
||||
self.config.set({"help_active": True,
|
||||
"color": 0x000000,
|
||||
"auth_everyone": False,
|
||||
"authorized_roles": [],
|
||||
"authorized_users": [],
|
||||
"command_text": self.name.lower()})
|
||||
self.config.load()
|
||||
|
||||
async def send_help(self, channel):
|
||||
embed = discord.Embed(
|
||||
title="[{nom}] - Aide".format(nom=self.name),
|
||||
description="*" + self.help["description"].format(prefix=self.client.config['prefix']) + "*",
|
||||
color=self.config["color"]
|
||||
)
|
||||
for command, description in self.help["commands"].items():
|
||||
embed.add_field(
|
||||
name=command.format(prefix=self.client.config['prefix'], command=self.config["command_text"]),
|
||||
value="-> " + description.format(prefix=self.client.config['prefix'],
|
||||
command=self.config["command_text"]),
|
||||
inline=False)
|
||||
await channel.send(embed=embed)
|
||||
|
||||
def auth(self, user: discord.User, role_list: List[int] = None, user_list: List[int] = None,
|
||||
guild: int = None):
|
||||
"""
|
||||
Return True if user is an owner of the bot or in authorized_users or he have a role in authorized_roles.
|
||||
|
||||
:param user: User to check
|
||||
:param user_list: List of authorized users, if not specified use self.authorized_users
|
||||
:param role_list: list of authorized roles, if not specified use self.authorized_roles
|
||||
:param guild: Specific guild to search role
|
||||
:type user_list: List[Int]
|
||||
:type role_list: List[Int]
|
||||
:type guild: Int
|
||||
:type user: discord.User
|
||||
"""
|
||||
if self.config["auth_everyone"]:
|
||||
return True
|
||||
if user_list is None:
|
||||
user_list = self.config["authorized_users"] + self.client.config['admin_users']
|
||||
if user.id in user_list:
|
||||
return True
|
||||
if role_list is None:
|
||||
role_list = self.config["authorized_roles"] + self.client.config['admin_roles']
|
||||
if guild is None:
|
||||
guilds = self.client.guilds
|
||||
else:
|
||||
guilds = [guild]
|
||||
for guild in guilds:
|
||||
if guild.get_member(user.id):
|
||||
for role_id in role_list:
|
||||
if role_id in [r.id for r in guild.get_member(user.id).roles]:
|
||||
return True
|
||||
return False
|
||||
|
||||
async def parse_command(self, message):
|
||||
"""Parse a command_text from received message and execute function
|
||||
Parse message like `{prefix}{command_text} subcommand` and call class method `com_{subcommand}`.
|
||||
|
||||
:param message: message to parse
|
||||
:type message: discord.Message"""
|
||||
command = self.client.config["prefix"] + (self.config["command_text"] if self.config["command_text"] else "")
|
||||
if message.content.startswith(command):
|
||||
content = message.content.split(" ", 1)[1 if " " in message.content else 0]
|
||||
sub_command, args, kwargs = self._parse_command_content(content)
|
||||
sub_command = "com_" + sub_command
|
||||
if self.auth(message.author):
|
||||
if sub_command in dir(self):
|
||||
await self.__getattribute__(sub_command)(message, args, kwargs)
|
||||
else:
|
||||
await self.command(message, args, kwargs)
|
||||
else:
|
||||
await self.unauthorized(message)
|
||||
|
||||
@staticmethod
|
||||
def _parse_command_content(content):
|
||||
"""Parse string
|
||||
|
||||
Parse string like `subcommand argument "argument with spaces" -o -shortwaytopassoncharacteroption --longoption
|
||||
-o "option with argument"`. You can override this function to change parsing.
|
||||
|
||||
:param content: content to parse
|
||||
:type content: str
|
||||
|
||||
:return: parsed arguments: [subcommand, [arg1, arg2, ...], [(option1, arg1), (option2, arg2), ...]]
|
||||
:rtype: tuple[str, list, list]"""
|
||||
if not len(content.split()):
|
||||
return "", [], []
|
||||
# Sub_command
|
||||
sub_command = content.split()[0]
|
||||
args_ = [sub_command]
|
||||
kwargs = []
|
||||
if len(content.split()) > 1:
|
||||
# Remove subcommand
|
||||
content = content.lstrip(sub_command)
|
||||
# Take the other part of command_text
|
||||
content = content.lstrip().replace("\"", "\"\"")
|
||||
# Splitting around quotes
|
||||
quotes = [element.split("\" ") for element in content.split(" \"")]
|
||||
# Split all sub chains but raw chains and flat the resulting list
|
||||
args = [item.split() if item[0] != "\"" else [item, ] for sublist in quotes for item in sublist]
|
||||
# Second plating
|
||||
args = [item for sublist in args for item in sublist]
|
||||
# args_ are arguments, kwargs are options with arguments
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i].startswith("\""):
|
||||
args_.append(args[i][1:-1])
|
||||
elif args[i].startswith("--"):
|
||||
if i + 1 >= len(args):
|
||||
kwargs.append((args[i].lstrip("-"), None))
|
||||
break
|
||||
if args[i + 1][0] != "-":
|
||||
kwargs.append((args[i].lstrip("-"), args[i + 1].strip("\"")))
|
||||
i += 1
|
||||
else:
|
||||
kwargs.append((args[i].lstrip("-"), None))
|
||||
elif args[i].startswith("-"):
|
||||
if len(args[i]) == 2:
|
||||
if i + 1 >= len(args):
|
||||
break
|
||||
if args[i + 1][0] != "-":
|
||||
kwargs.append((args[i].lstrip("-"), args[i + 1].strip("\"")))
|
||||
i += 1
|
||||
else:
|
||||
kwargs.append((args[i].lstrip("-"), None))
|
||||
else:
|
||||
kwargs.extend([(arg, None) for arg in args[i][1:]])
|
||||
else:
|
||||
args_.append(args[i])
|
||||
i += 1
|
||||
return sub_command, args_, kwargs
|
||||
|
||||
async def on_message(self, message: discord.Message):
|
||||
"""Override this function to deactivate command_text parsing"""
|
||||
if message.author.bot:
|
||||
return
|
||||
await self.parse_command(message)
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
"""Override this function to handle all messages starting with `{prefix}{command_text}`
|
||||
|
||||
Function which is executed for all command_text doesn't match with a `com_{subcommand}` function"""
|
||||
await self.send_help(message.channel)
|
||||
|
||||
async def com_help(self, message, args, kwargs):
|
||||
await self.send_help(message.channel)
|
||||
|
||||
async def unauthorized(self, message):
|
||||
await message.channel.send("Vous n'êtes pas autorisé à effectuer cette commande")
|
||||
|
||||
def dispatch(self, event, *args, **kwargs):
|
||||
# Method to call
|
||||
method = 'on_' + event
|
||||
try:
|
||||
# Try to get coro, if not exists pass without raise an error
|
||||
coro = getattr(self, method)
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
# Run event
|
||||
asyncio.ensure_future(self.client._run_event(coro, method, *args, **kwargs), loop=self.client.loop)
|
||||
|
||||
async def on_error(self, event_method, *args, **kwargs):
|
||||
pass
|
||||
|
||||
async def choice(self, message: discord.Message, choices: List[Union[discord.Emoji, discord.PartialEmoji, str]],
|
||||
validation: bool = False,
|
||||
validation_emote: Union[discord.Emoji, discord.PartialEmoji, str] = emojis.WHITE_CHECK_MARK,
|
||||
minimal_choices: int = 1,
|
||||
maximal_choices: Optional[int] = None,
|
||||
timeout: Optional[float] = None,
|
||||
user: Optional[discord.User] = None,
|
||||
unique: bool = False):
|
||||
final_choices: List[Union[discord.Emoji, discord.PartialEmoji, str]] = []
|
||||
validation_step = False
|
||||
for emoji in choices:
|
||||
await message.add_reaction(emoji)
|
||||
|
||||
def check_add(reaction, u):
|
||||
nonlocal validation_step, final_choices
|
||||
if (not user.bot) and (user is None or u.id == user.id):
|
||||
if validation_step and reaction.emoji == validation_emote:
|
||||
return True
|
||||
if reaction in choices:
|
||||
if not unique or reaction.emoji not in final_choices:
|
||||
final_choices.append(reaction.emoji)
|
||||
if maximal_choices is not None and len(final_choices) > maximal_choices:
|
||||
validation_step = False
|
||||
asyncio.ensure_future(message.remove_reaction(validation_emote, self.client.user))
|
||||
try:
|
||||
asyncio.get_running_loop().run_until_complete(message.clear_reaction(validation_emote))
|
||||
except discord.errors.Forbidden:
|
||||
pass
|
||||
return False
|
||||
if len(final_choices) >= minimal_choices:
|
||||
if validation:
|
||||
asyncio.get_running_loop().run_until_complete(message.add_reaction(validation_emote))
|
||||
validation_step = True
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_remove(reaction: discord.Reaction, u):
|
||||
nonlocal validation_step, final_choices
|
||||
if (not user.bot) and (user is None or u.id == user.id):
|
||||
if reaction.emoji in choices:
|
||||
if not unique or reaction.count != 0:
|
||||
final_choices.remove(reaction.emoji)
|
||||
if len(final_choices) < minimal_choices:
|
||||
if validation_step:
|
||||
asyncio.ensure_future(message.remove_reaction(validation_emote, self.client.user))
|
||||
try:
|
||||
asyncio.get_running_loop().run_until_complete(message.clear_reaction(validation_emote))
|
||||
except discord.errors.Forbidden:
|
||||
pass
|
||||
validation_step = False
|
||||
return False
|
||||
if (maximal_choices is None or len(final_choices) <= maximal_choices) and len(
|
||||
final_choices) >= minimal_choices:
|
||||
if validation:
|
||||
asyncio.get_running_loop().run_until_complete(message.add_reaction(validation_emote))
|
||||
validation_step = True
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
done, pending = await asyncio.wait([
|
||||
self.client.wait_for('reaction_add', timeout=timeout, check=check_add),
|
||||
self.client.wait_for('reaction_remove', timeout=timeout, check=check_remove)],
|
||||
return_when=asyncio.FIRST_COMPLETED)
|
||||
return final_choices
|
@ -1,73 +0,0 @@
|
||||
"""Base class for module, never use directly !!!"""
|
||||
import asyncio
|
||||
|
||||
import discord
|
||||
import lupa
|
||||
|
||||
from modules.base.Base import BaseClass
|
||||
|
||||
|
||||
class BaseClassLua(BaseClass):
|
||||
"""Base class for all modules, Override it to make submodules"""
|
||||
name = ""
|
||||
help = {
|
||||
"description": "",
|
||||
"commands": {
|
||||
|
||||
}
|
||||
}
|
||||
help_active = False
|
||||
color = 0x000000
|
||||
command_text = None
|
||||
authorized_users = []
|
||||
authorized_roles = []
|
||||
command_text = ""
|
||||
|
||||
def __init__(self, client, path):
|
||||
"""Initialize module class
|
||||
|
||||
Initialize module class, always call it to set self.client when you override it.
|
||||
|
||||
:param client: client instance
|
||||
:type client: NikolaTesla"""
|
||||
super().__init__(client)
|
||||
# Get lua globals
|
||||
self.lua = lupa.LuaRuntime(unpack_returned_tuples=True)
|
||||
self.luaMethods = self.lua.require(path)
|
||||
|
||||
def call(self, method, *args, **kwargs):
|
||||
# Try to run lua method then python one
|
||||
if self.luaMethods[method] is not None:
|
||||
async def coro(*args, **kwargs):
|
||||
self.luaMethods[method](self, asyncio.ensure_future, discord, *args, *kwargs)
|
||||
asyncio.ensure_future(self.client._run_event(coro, method, *args, **kwargs), loop=self.client.loop)
|
||||
try:
|
||||
coro = getattr(self, method)
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
asyncio.ensure_future(self.client._run_event(coro, method, *args, **kwargs), loop=self.client.loop)
|
||||
|
||||
def dispatch(self, event, *args, **kwargs):
|
||||
method = "on_"+event
|
||||
self.call(method, *args, **kwargs)
|
||||
|
||||
async def parse_command(self, message):
|
||||
"""Parse a command_text from received message and execute function
|
||||
%git update
|
||||
com_update(m..)
|
||||
Parse message like `{prefix}{command_text} subcommand` and call class method `com_{subcommand}`.
|
||||
|
||||
:param message: message to parse
|
||||
:type message: discord.Message"""
|
||||
if message.content.startswith(self.client.config["prefix"] + (self.command_text if self.command_text else "")):
|
||||
|
||||
content = message.content.lstrip(
|
||||
self.client.config["prefix"] + (self.command_text if self.command_text else ""))
|
||||
sub_command, args, kwargs = self._parse_command_content(content)
|
||||
sub_command = "com_" + sub_command
|
||||
if self.auth(message.author):
|
||||
self.call(sub_command, args, kwargs)
|
||||
else:
|
||||
await self.unauthorized(message)
|
||||
|
@ -1,8 +0,0 @@
|
||||
"""Base class for module, never use directly !!!"""
|
||||
|
||||
from modules.base.Base import BaseClass
|
||||
|
||||
|
||||
class BaseClassPython(BaseClass):
|
||||
"""Base class for all modules, Override it to make submodules"""
|
||||
pass
|
@ -1,3 +0,0 @@
|
||||
from .BasePython import BaseClassPython
|
||||
from .BaseLua import BaseClassLua
|
||||
base_supported_type = ["python", "lua"]
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "clean"
|
||||
help = {
|
||||
"description": "Supprime des messages",
|
||||
"commands": {
|
||||
"`{prefix}{command}`": "Supprime tous les messages du bot dans le salon"
|
||||
}
|
||||
}
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
def is_me(m):
|
||||
return m.author == self.client.user
|
||||
|
||||
deleted = await message.channel.purge(limit=10000000, check=is_me)
|
||||
await message.channel.send('Deleted {} message(s)'.format(len(deleted)))
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
import asyncio
|
||||
import random
|
||||
import traceback
|
||||
|
||||
import discord
|
||||
from discord import Message
|
||||
|
||||
from config import config_types
|
||||
from config.config_types import factory
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "errors"
|
||||
authorized_users = []
|
||||
authorized_roles = []
|
||||
help = {
|
||||
"description": "Montre toutes les erreurs du bot dans discord_types.",
|
||||
"commands": {
|
||||
"`{prefix}{command}`": "Renvoie une erreur de test.",
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.config.register("dev_chan",
|
||||
factory(config_types.List, factory(config_types.discord_types.Channel, client)))
|
||||
self.config.register("memes", factory(config_types.List, factory(config_types.Str)))
|
||||
self.config.register("icon", factory(config_types.Str))
|
||||
self.config.set({"dev_chan": [], "memes": [""], "icon": ""})
|
||||
self.errorsList = None
|
||||
|
||||
async def on_load(self):
|
||||
if self.objects.save_exists('errorsList'):
|
||||
self.errorsList = self.objects.load_object('errorsList')
|
||||
else:
|
||||
self.errorsList = []
|
||||
|
||||
async def on_ready(self):
|
||||
for i in range(len(self.errorsList)):
|
||||
try:
|
||||
msg_id = self.errorsList.pop(0)
|
||||
channel = self.client.get_channel(msg_id["channel_id"])
|
||||
to_delete = await channel.fetch_message(msg_id["msg_id"])
|
||||
await to_delete.delete()
|
||||
except:
|
||||
raise
|
||||
self.objects.save_object('errorsList', self.errorsList)
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
raise Exception("KERNEL PANIC!!!")
|
||||
|
||||
async def on_error(self, event, *args, **kwargs):
|
||||
"""Send error message"""
|
||||
# Search first channel instance found in arg, then search in kwargs
|
||||
channel = None
|
||||
for arg in args:
|
||||
if type(arg) == Message:
|
||||
channel = arg.channel
|
||||
break
|
||||
if type(arg) == discord.TextChannel:
|
||||
channel = arg
|
||||
break
|
||||
if channel is None:
|
||||
for _, v in kwargs.items():
|
||||
if type(v) == discord.Message:
|
||||
channel = v.channel
|
||||
break
|
||||
if type(v) == discord.TextChannel:
|
||||
channel = v
|
||||
break # Create embed
|
||||
embed = discord.Embed(
|
||||
title="[Erreur] Aïe :/",
|
||||
description="```python\n{0}```".format(traceback.format_exc()),
|
||||
color=self.config["color"])
|
||||
embed.set_image(url=random.choice(self.config["memes"]))
|
||||
message_list = None
|
||||
|
||||
# Send message to dev channels
|
||||
for chanid in self.config["dev_chan"]:
|
||||
try:
|
||||
await self.client.get_channel(chanid).send(
|
||||
embed=embed.set_footer(text="Ce message ne s'autodétruira pas.", icon_url=self.config["icon"]))
|
||||
except BaseException as e:
|
||||
raise e
|
||||
# Send message to current channel if exists
|
||||
if channel is not None:
|
||||
message = await channel.send(embed=embed.set_footer(text="Ce message va s'autodétruire dans une minute",
|
||||
icon_url=self.config["icon"]))
|
||||
msg_id = {"channel_id": message.channel.id, "msg_id": message.id}
|
||||
self.errorsList.append(msg_id)
|
||||
# Save message in errorsList now to keep them if a reboot happend during next 60 seconds
|
||||
self.objects.save_object('errorsList', self.errorsList)
|
||||
|
||||
# Wait 60 seconds and delete message
|
||||
# await asyncio.sleep(60)
|
||||
try:
|
||||
# channel = self.client.get_channel(msg_id["channel_id"])
|
||||
# delete_message = await channel.fetch_message(msg_id["msg_id"])
|
||||
# await delete_message.delete()
|
||||
await message.add_reaction("🗑️")
|
||||
|
||||
try:
|
||||
reaction, user = await self.client.wait_for('reaction_add',
|
||||
timeout=60.0,
|
||||
check=lambda r, u:
|
||||
r.emoji == "🗑️" and not u.bot and self.auth(u))
|
||||
except asyncio.TimeoutError:
|
||||
await message.delete()
|
||||
else:
|
||||
await reaction.message.delete()
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
try:
|
||||
self.errorsList.remove(msg_id)
|
||||
except ValueError:
|
||||
pass
|
||||
# Save now to avoid deleting unkown message
|
||||
self.objects.save_object('errorsList', self.errorsList)
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
"base": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import discord
|
||||
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "Aide"
|
||||
help = {
|
||||
"description": "Module d'aide",
|
||||
"commands": {
|
||||
"`{prefix}{command} list`": "Affiche une liste des modules ainsi qu'une desription",
|
||||
"`{prefix}{command} <module>`": "Affiche l'aide sépcifique d'un module"# ,
|
||||
# "`{prefix}{command} all`": "Affiche l'aide de tous les modules"
|
||||
}
|
||||
}
|
||||
|
||||
async def com_list(self, message, args, kwargs):
|
||||
embed = discord.Embed(title="[Aide] - Liste des modules", color=self.config.color)
|
||||
for moduleName in list(self.client.modules.keys()):
|
||||
if self.client.modules[moduleName]["initialized_class"].config.help_active:
|
||||
embed.add_field(
|
||||
name=moduleName.capitalize(),
|
||||
value=self.client.modules[moduleName]["initialized_class"].help["description"])
|
||||
await message.channel.send(embed=embed)
|
||||
|
||||
# async def com_all(self, message, args, kwargs):
|
||||
# for name, module in self.client.modules.items():
|
||||
# await module["initialized_class"].send_help(message.channel)
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
if len(args) and args[0] in self.client.modules.keys() and self.client.modules[args[0]][
|
||||
"initialized_class"].config.help_active:
|
||||
await self.client.modules[args[0]]["initialized_class"].send_help(message.channel)
|
||||
else :
|
||||
await self.send_help(message.channel)
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
import os
|
||||
|
||||
import discord
|
||||
from aiohttp import ClientConnectorError
|
||||
|
||||
from modules.base import BaseClassPython
|
||||
from modules.modules.api import Api
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "modules"
|
||||
help = {
|
||||
"description": "Manage bot modules.",
|
||||
"commands": {
|
||||
"`{prefix}{command} list`": "List of available modules.",
|
||||
"`{prefix}{command} enable <module>`": "Enable module `<module>`.",
|
||||
"`{prefix}{command} disable <module>`": "Disable module `<module>`.",
|
||||
"`{prefix}{command} reload <module>`": "Reload module `<module>`",
|
||||
"`{prefix}{command} web_list`": "List all available modules from repository",
|
||||
# "`{prefix}{command} web_source`": "List all source repositories",
|
||||
# "`{prefix}{command} web_source remove <url>`": "Remove url from repository list",
|
||||
# "`{prefix}{command} web_source add <url>`": "Add url to repository list",
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
os.makedirs("modules", exist_ok=True)
|
||||
self.api = Api()
|
||||
|
||||
@staticmethod
|
||||
def get_all_modules():
|
||||
all_items = os.listdir("modules")
|
||||
modules = []
|
||||
for item in all_items:
|
||||
if item not in ["__init__.py", "base", "__pycache__"]:
|
||||
if os.path.isfile(os.path.join("modules", item)):
|
||||
modules.append(item[:-3])
|
||||
else:
|
||||
modules.append(item)
|
||||
return set(modules)
|
||||
|
||||
async def com_enable(self, message, args, kwargs):
|
||||
args = args[1:]
|
||||
if len(args) == 0:
|
||||
await message.channel.send("You must specify at least one module.")
|
||||
return
|
||||
if len(args) == 1 and args[0] == "*":
|
||||
for module in self.get_all_modules():
|
||||
e = self.client.load_module(module)
|
||||
if e:
|
||||
await message.channel.send("An error occurred during the loading of the module {module}."
|
||||
.format(module=module))
|
||||
await self.com_list(message, args, kwargs)
|
||||
return
|
||||
for arg in args:
|
||||
e = self.client.load_module(arg)
|
||||
if e == 1:
|
||||
await message.channel.send(f"Module {arg} not exists.")
|
||||
if e == 2:
|
||||
await message.channel.send(f"Module {arg} is incompatible.")
|
||||
elif e:
|
||||
await message.channel.send(f"An error occurred during the loading of the module {arg}: {e}.")
|
||||
await self.com_list(message, args, kwargs)
|
||||
|
||||
async def com_reload(self, message, args, kwargs):
|
||||
args = args[1:]
|
||||
if len(args) == 0:
|
||||
await message.channel.send("You must specify at least one module.")
|
||||
return
|
||||
if len(args) == 1 and args[0] == "*":
|
||||
for module in self.get_all_modules():
|
||||
e = self.client.unload_module(module)
|
||||
if e:
|
||||
await message.channel.send(f"An error occurred during the unloading of the module {module}.")
|
||||
e = self.client.load_module(module)
|
||||
if e:
|
||||
await message.channel.send(f"An error occurred during the loading of the module {module}.")
|
||||
await self.com_list(message, args, kwargs)
|
||||
return
|
||||
for arg in args:
|
||||
e = self.client.unload_module(arg)
|
||||
if e:
|
||||
await message.channel.send(f"An error occurred during the unloading of the module {arg}.")
|
||||
e = self.client.load_module(arg)
|
||||
if e:
|
||||
await message.channel.send(f"An error occurred during the loading of the module {arg}.")
|
||||
await self.com_list(message, [], [])
|
||||
|
||||
async def com_disable(self, message, args, kwargs):
|
||||
args = args[1:]
|
||||
if len(args) == 0:
|
||||
await message.channel.send("You must specify at least one module.")
|
||||
return
|
||||
if len(args) == 1 and args[0] == "*":
|
||||
for module in self.get_all_modules():
|
||||
e = self.client.unload_module(module)
|
||||
if e:
|
||||
await message.channel.send(f"An error occurred during the loading of the module {module}.")
|
||||
await self.com_list(message, args, kwargs)
|
||||
return
|
||||
for arg in args:
|
||||
e = self.client.unload_module(arg)
|
||||
if e:
|
||||
await message.channel.send(f"An error occurred during the loading of the module {arg}: {e}.")
|
||||
await self.com_list(message, [], [])
|
||||
|
||||
async def com_list(self, message, args, kwargs):
|
||||
list_files = self.get_all_modules()
|
||||
activated = set(self.client.config["modules"])
|
||||
if len(activated):
|
||||
activated_string = "\n+ " + "\n+ ".join(activated)
|
||||
else:
|
||||
activated_string = ""
|
||||
if len(activated) != len(list_files):
|
||||
deactivated_string = "\n- " + "\n- ".join(list_files.difference(activated))
|
||||
else:
|
||||
deactivated_string = ""
|
||||
embed = discord.Embed(title="[Modules] - Liste des modules",
|
||||
description="```diff{activated}{deactivated}```".format(
|
||||
activated=activated_string,
|
||||
deactivated=deactivated_string)
|
||||
)
|
||||
await message.channel.send(embed=embed)
|
||||
|
||||
async def com_web_list(self, message, args, kwargs):
|
||||
try:
|
||||
modules = await self.api.list()
|
||||
except ClientConnectorError:
|
||||
await message.channel.send("Connection impossible au serveur.")
|
||||
return
|
||||
text = ""
|
||||
for module, versions in modules.items():
|
||||
text += module + " - " + ", ".join(versions)
|
||||
await message.channel.send(text)
|
||||
|
||||
async def com_web_dl(self, message, args, kwargs):
|
||||
try:
|
||||
await self.api.download(args[1], args[2])
|
||||
except ClientConnectorError:
|
||||
await message.channel.send("Connection impossible au serveur.")
|
@ -1,40 +0,0 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import aiohttp
|
||||
import aiofiles
|
||||
import zipfile
|
||||
|
||||
class Api:
|
||||
def __init__(self, host="localhost:5000"):
|
||||
self.host = host
|
||||
self.basepath = "http://"+host+"/api/current"
|
||||
|
||||
async def _get(self, endpoint):
|
||||
if endpoint[0] != "/":
|
||||
endpoint = "/" + endpoint
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(self.basepath+endpoint) as response:
|
||||
return await response.json()
|
||||
|
||||
async def _download(self, endpoint, filename="temp"):
|
||||
if endpoint[0] != "/":
|
||||
endpoint = "/" + endpoint
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(self.basepath+endpoint) as resp:
|
||||
f = await aiofiles.open(filename, mode='wb')
|
||||
await f.write(await resp.read())
|
||||
await f.close()
|
||||
|
||||
async def list(self):
|
||||
return await self._get("modules/")
|
||||
|
||||
async def download(self, module, version):
|
||||
await self._download("modules/"+module+"/"+version, filename="temp.zip")
|
||||
# TODO: Supprimer le dossier ici
|
||||
try:
|
||||
shutil.rmtree(os.path.join("modules", module))
|
||||
except:
|
||||
print('Error while deleting directory')
|
||||
with zipfile.ZipFile('temp.zip', "r") as z:
|
||||
z.extractall(os.path.join("modules", module))
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
"base": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "NewMember"
|
||||
help = {
|
||||
"description": "Module d'accueil",
|
||||
"commands": {
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.config.set({"new_role": 0,
|
||||
"motd": "Bienvenue !"})
|
||||
|
||||
async def on_ready(self):
|
||||
guild = self.client.get_guild(self.client.config.main_guild)
|
||||
for i, member in enumerate(guild.members):
|
||||
if len(member.roles) == 1:
|
||||
await member.add_roles(self.client.id.get_role(id_=self.config.new_role,
|
||||
guilds=[self.client.get_guild(
|
||||
self.client.config.main_guild)]))
|
||||
if i % 50 == 0:
|
||||
self.client.log(f"Attribution des roles automatique manqués... {i}/{len(guild.members)}")
|
||||
|
||||
async def on_member_join(self, member):
|
||||
await member.add_roles(self.client.id.get_role(id_=self.config.new_role,
|
||||
guilds=[self.client.get_guild(
|
||||
self.client.config.main_guild)]))
|
||||
await member.send(self.config.motd)
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
import time
|
||||
|
||||
import discord
|
||||
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "Panic"
|
||||
help = {
|
||||
"description": "Dans quel état est Nikola Tesla",
|
||||
"commands": {
|
||||
"`{prefix}{command}`": "Donne l'état actuel de Nikola Tesla",
|
||||
}
|
||||
}
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
temperature = 0
|
||||
with open("/sys/class/thermal/thermal_zone0/temp") as f:
|
||||
temperature = int(f.read().rstrip("\n")) / 1000
|
||||
with open("/proc/cpuinfo") as f:
|
||||
cpu_count = f.read().count('\n\n')
|
||||
embed = discord.Embed(title="[Panic] - Infos", color=self.config.color)
|
||||
with open("/proc/loadavg") as f:
|
||||
load_average = ["**" + str(round((val / cpu_count) * 100, 1)) + '%**' for val in
|
||||
map(float, f.read().split(' ')[0:3])]
|
||||
with open("/proc/uptime") as f:
|
||||
uptime = time.gmtime(float(f.read().split(' ')[0]))
|
||||
uptime = "**" + str(int(time.strftime('%-m', uptime)) - 1) + "** mois, **" + str(
|
||||
int(time.strftime('%-d', uptime)) - 1) + "** jours, " + time.strftime(
|
||||
'**%H** heures, **%M** minutes, **%S** secondes.', uptime)
|
||||
embed.add_field(
|
||||
name="Température",
|
||||
value="Nikola est à **{temperature}°C**".format(temperature=temperature))
|
||||
|
||||
embed.add_field(
|
||||
name="Charge moyenne",
|
||||
value=f"{self.client.name} est en moyenne, utilisé à :\n sur une minute : %s\n sur cinq minutes : %s\n sur quinze minutes : %s" % tuple(
|
||||
load_average))
|
||||
|
||||
embed.add_field(
|
||||
name="Temps d'éveil",
|
||||
value=f"{self.client.name} est éveillé depuis {uptime}".format(uptime=uptime))
|
||||
await message.channel.send(embed=embed)
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
import datetime
|
||||
import time
|
||||
|
||||
import discord
|
||||
import humanize
|
||||
import matplotlib.pyplot as np
|
||||
|
||||
import config
|
||||
import utils.emojis
|
||||
from config.config_types import factory
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "Perdu"
|
||||
help = {
|
||||
"description": "Module donnant les statistiques sur les perdants",
|
||||
"commands": {
|
||||
"`{prefix}{command}`": "Donne le classement des perdants de la semaine",
|
||||
"`{prefix}{command} all`": "Donne le classement des perdants depuis toujours",
|
||||
"`{prefix}{command} <nombre de jours>`": "Donne le classement des perdants sur la durée spécifiée",
|
||||
"`{prefix}{command} stats [@mention]`": "Donne les statistiques d'un perdant.",
|
||||
"`{prefix}{command} stats history": "Affiche un graphique avec le nombre de pertes."
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.config.set({"channel": 0, "lost_role": 0, "min_delta": datetime.timedelta(minutes=26).total_seconds()})
|
||||
self.config.register("channel", factory(config.config_types.Channel, self.client))
|
||||
self.history = {}
|
||||
|
||||
async def on_ready(self):
|
||||
await self.fill_history()
|
||||
|
||||
async def on_message(self, message: discord.Message):
|
||||
# Fill history
|
||||
if message.author.bot:
|
||||
return
|
||||
if message.channel.id == self.config.channel:
|
||||
if message.author.id not in self.history.keys():
|
||||
# Add new user if not found
|
||||
self.history.update(
|
||||
{message.author.id: ([(message.created_at, datetime.timedelta(seconds=0)), ])}
|
||||
)
|
||||
else:
|
||||
# Update user and precompute timedelta
|
||||
delta = message.created_at - self.history[message.author.id][-1][0]
|
||||
if delta.total_seconds() >= self.config.min_delta:
|
||||
self.history[message.author.id].append((message.created_at, delta))
|
||||
await self.parse_command(message)
|
||||
|
||||
async def fill_history(self):
|
||||
self.history = {}
|
||||
async for message in self.client.get_channel(self.config.channel).history(limit=None):
|
||||
if message.author.id not in self.history.keys():
|
||||
# Add new user if not found
|
||||
self.history.update({message.author.id: ([(message.created_at, datetime.timedelta(seconds=0)), ])})
|
||||
else:
|
||||
# Update user and precompute timedelta
|
||||
delta = self.history[message.author.id][-1][0] - message.created_at
|
||||
if delta.total_seconds() >= self.config.min_delta:
|
||||
self.history[message.author.id].append((message.created_at, delta))
|
||||
for user in self.history.keys():
|
||||
self.history[user].sort(key=lambda x: x[0])
|
||||
|
||||
def get_top(self, top=10, since=datetime.datetime(year=1, month=1, day=1), with_user=None, only_users=None):
|
||||
"""Return [(userid, [(date, delta), (date,delta), ...]), ... ]"""
|
||||
# Extract only messages after until
|
||||
if only_users is not None:
|
||||
# Extract data for only_users
|
||||
messages = []
|
||||
for user in only_users:
|
||||
try:
|
||||
if self.history[user][-1][0] >= since:
|
||||
messages.append((user, [message for message in self.history[user] if message[0] > since]))
|
||||
except KeyError:
|
||||
pass
|
||||
messages.sort(key=lambda x: len(x[1]), reverse=True)
|
||||
return messages
|
||||
if with_user is None:
|
||||
with_user = []
|
||||
# Extract TOP top users, and with_users data
|
||||
messages = []
|
||||
for user in self.history.keys():
|
||||
if self.history[user][-1][0] >= since:
|
||||
messages.append((user, [message for message in self.history[user] if message[0] > since]))
|
||||
messages.sort(key=lambda x: len(x[1]), reverse=True)
|
||||
# Extract top-ten
|
||||
saved_messages = messages[:min(top, len(messages))]
|
||||
# Add with_user
|
||||
saved_messages.extend([message for message in messages if message[0] in with_user])
|
||||
return saved_messages
|
||||
|
||||
async def com_fill(self, message: discord.Message, args, kwargs):
|
||||
if self.auth(message.author):
|
||||
async with message.channel.typing():
|
||||
await self.fill_history()
|
||||
await message.channel.send("Fait.")
|
||||
|
||||
async def com_all(self, message: discord.Message, args, kwargs):
|
||||
# Get all stats
|
||||
top = self.get_top()
|
||||
intervales = [sum(list(zip(*top[i][1]))[1], datetime.timedelta(0)) / len(top[i][1]) for i in range(len(top))]
|
||||
embed_description = "\n".join(
|
||||
f"{utils.emojis.write_with_number(i)} : <@{top[i][0]}> a **perdu {len(top[i][1])} fois** depuis la"
|
||||
f" création du salon à en moyenne **"
|
||||
f"{(str(intervales[i].days) + ' jours et' if intervales[i].days else '')} "
|
||||
f"{str(int(intervales[i].total_seconds() % (24 * 3600) // 3600)) + ':' if intervales[i].total_seconds() > 3600 else ''}"
|
||||
f"{int((intervales[i].total_seconds() % 3600) // 60)} "
|
||||
f"{'heures' if intervales[i].total_seconds() > 3600 else 'minutes'} d'intervalle.**"
|
||||
for i in range(len(top))
|
||||
)[:2000]
|
||||
await message.channel.send(embed=discord.Embed(title="G-Perdu - Tableau des scores",
|
||||
description=embed_description,
|
||||
color=self.config.color))
|
||||
|
||||
async def com_stats(self, message: discord.Message, args, kwargs):
|
||||
# TODO: Finir sum
|
||||
async with message.channel.typing():
|
||||
if not ((not False or (not False or not ("sum" in args))) or not True):
|
||||
if message.mentions:
|
||||
top = self.get_top(only_users=[mention.id for mention in message.mentions] + [message.author.id])
|
||||
else:
|
||||
# TOP 5 + auteur
|
||||
top = self.get_top(top=5, with_user=[message.author.id])
|
||||
dates = []
|
||||
new_top = {}
|
||||
for t in top:
|
||||
for date, _ in t[1]:
|
||||
dates.append(date)
|
||||
dates.sort()
|
||||
dates.append(datetime.datetime.today() + datetime.timedelta(days=1))
|
||||
for t in top:
|
||||
user = t[0]
|
||||
new_top.update({user: ([dates[0]], [0])})
|
||||
i = 0
|
||||
for date, _ in t[1]:
|
||||
while date < dates[i]:
|
||||
new_top[user][0].append(dates[i])
|
||||
new_top[user][1].append(new_top[user][1][-1])
|
||||
i += 1
|
||||
new_top[user][0].append(date)
|
||||
new_top[user][1].append(new_top[user][1][-1] + 1)
|
||||
|
||||
to_plot = [t[1][1:] for t in new_top.values()]
|
||||
np.xlabel("Temps", fontsize=30)
|
||||
np.ylabel("Score", fontsize=30)
|
||||
np.title("Évolution du nombre de perdu au cours du temps.", fontsize=40)
|
||||
np.legend()
|
||||
file_name = f"/tmp/{time.time()}.png"
|
||||
np.savefig(file_name, bbox_inches='tight')
|
||||
await message.channel.send(file=discord.File(file_name))
|
||||
|
||||
if "history" in args:
|
||||
since = datetime.datetime(year=1, month=1, day=1)
|
||||
debut_message = "la création du salon"
|
||||
top = 5
|
||||
if "s" in [k[0] for k in kwargs]:
|
||||
try:
|
||||
d = [k[1] for k in kwargs if k[0] == "s"][0]
|
||||
since = datetime.datetime.now() - datetime.timedelta(days=float(d))
|
||||
debut_message = humanize.naturalday(since.date(), format='le %d %b')
|
||||
except ValueError:
|
||||
pass
|
||||
if "t" in [k[0] for k in kwargs]:
|
||||
try:
|
||||
top = int([k[1] for k in kwargs if k[0] == "t"][0])
|
||||
except ValueError:
|
||||
pass
|
||||
# Si mention, alors uniquement les mentions
|
||||
if message.mentions:
|
||||
top = self.get_top(since=since,
|
||||
only_users=[mention.id for mention in message.mentions])
|
||||
else:
|
||||
# TOP 5 + auteur
|
||||
top = self.get_top(since=since, top=top, with_user=[message.author.id])
|
||||
new_top = {}
|
||||
for t in top:
|
||||
c = 0
|
||||
counts = []
|
||||
dates = []
|
||||
for date, _ in t[1]:
|
||||
c += 1
|
||||
counts.append(c)
|
||||
dates.append(date)
|
||||
new_top.update({t[0]: (dates, counts)})
|
||||
np.figure(num=None, figsize=(25, 15), dpi=120, facecolor='w', edgecolor='k')
|
||||
for user, (dates, counts) in new_top.items():
|
||||
np.plot_date(dates, counts, linestyle='-', label=str(self.client.get_user(user).name))
|
||||
np.xlabel("Temps", fontsize=30)
|
||||
np.ylabel("Score", fontsize=30)
|
||||
np.legend(fontsize=20)
|
||||
np.title(f"Évolution du nombre de perdu au cours du temps depuis {debut_message}.", fontsize=35)
|
||||
file_name = f"/tmp/{time.time()}.png"
|
||||
np.savefig(file_name, bbox_inches='tight')
|
||||
await message.channel.send(file=discord.File(file_name))
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
if message.mentions:
|
||||
await self.com_stats(message, args, kwargs)
|
||||
since = datetime.datetime.now() - datetime.timedelta(days=7)
|
||||
if args[0]:
|
||||
try:
|
||||
since = datetime.datetime.now() - datetime.timedelta(days=float(args[0]))
|
||||
except ValueError:
|
||||
pass
|
||||
top = self.get_top(10, since)
|
||||
intervales = [sum(list(zip(*top[i][1]))[1], datetime.timedelta(0)) / len(top[i][1]) for i in range(len(top))]
|
||||
embed_description = "\n".join(
|
||||
f"{utils.emojis.write_with_number(i)} : <@{top[i][0]}> a **perdu {len(top[i][1])} fois** depuis "
|
||||
f"{humanize.naturalday(since.date(), format='le %d %b')} à en moyenne **"
|
||||
f"{(str(intervales[i].days) + ' jours et' if intervales[i].days else '')} "
|
||||
f"{str(int(intervales[i].total_seconds() % (24 * 3600) // 3600)) + ':' if intervales[i].total_seconds() > 3600 else ''}"
|
||||
f"{int((intervales[i].total_seconds() % 3600) // 60)} "
|
||||
f"{'heures' if intervales[i].total_seconds() > 3600 else 'minutes'} d'intervalle.**"
|
||||
for i in range(len(top))
|
||||
)[:2000]
|
||||
await message.channel.send(embed=discord.Embed(title="G-Perdu - Tableau des scores",
|
||||
description=embed_description,
|
||||
color=self.config.color))
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "Purge"
|
||||
help = {
|
||||
"description": "Suppression de messages en block.",
|
||||
"commands": {
|
||||
"`{prefix}{command} <message_id>`": "Supprime tous les messages du salon jusqu'au message spécifié",
|
||||
}
|
||||
}
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
message_id = None
|
||||
try:
|
||||
message_id = int(args[0])
|
||||
except ValueError:
|
||||
pass
|
||||
if len(args) and message_id is not None:
|
||||
messages_list=[]
|
||||
done=False
|
||||
async for current in message.channel.history(limit=None):
|
||||
if int(current.id) == message_id:
|
||||
done = True
|
||||
break
|
||||
elif message.id != current.id:
|
||||
messages_list.append(current)
|
||||
if done:
|
||||
chunks = [messages_list[x:x+99] for x in range(0, len(messages_list), 99)]
|
||||
for chunk in chunks:
|
||||
await message.channel.delete_messages(chunk)
|
||||
await message.channel.send(f"**{len(messages_list)}** messages supprimés.")
|
||||
else:
|
||||
await message.channel.send("Le message spécifié n'a pas été trouvé.")
|
||||
else:
|
||||
await message.channel.send("Arguments invalides.")
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "ReadRules"
|
||||
color = 0xff071f
|
||||
help_active = False
|
||||
help = {
|
||||
"description": "Module d'accueil",
|
||||
"commands": {
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.config.set({"accepted_role": 0,
|
||||
"new_role": 0,
|
||||
"listen_chan": 0,
|
||||
"log_chan": 0,
|
||||
"passwords": [],
|
||||
"succes_pm": "Félicitations, vous savez lire les règles!",
|
||||
"succes": "{user} a désormais accepté."})
|
||||
|
||||
async def on_message(self, message):
|
||||
if message.author.bot:
|
||||
return
|
||||
if message.channel.id == self.config.listen_chan:
|
||||
if message.content.lower() in self.config.passwords:
|
||||
new_role = self.client.id.get_role(id_=self.config.new_role, guilds=[message.channel.guild])
|
||||
if new_role in message.author.roles:
|
||||
await message.author.remove_roles(new_role)
|
||||
await message.author.add_roles(self.client.id.get_role(id_=self.config.accepted_role,
|
||||
guild=[message.channel.guild]))
|
||||
await message.author.send(self.config.succes_pm)
|
||||
await message.channel.guild.get_channel(self.config.log_chan).send(
|
||||
self.config.succes.format(user=message.author.mention))
|
||||
else:
|
||||
await message.author.send(f"Le mot de passe que vous avez entré est incorrect : `{message.content}`.\nNous vous prions de lire le règlement afin d'accéder au serveur complet.")
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import sys
|
||||
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "Restart"
|
||||
help = {
|
||||
"description": "Module gérant les redémarrages de Nikola Tesla",
|
||||
"commands": {
|
||||
"`{prefix}{command}`": "Redémarre le bot.",
|
||||
}
|
||||
}
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
await message.channel.send(f"{message.author.mention}, Le bot va redémarrer.")
|
||||
await self.client.logout()
|
||||
# TODO: Faut vraiment faire mieux
|
||||
sys.exit(0)
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
import discord
|
||||
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class RoleAttributionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AlreadyHasRoleError(RoleAttributionError):
|
||||
pass
|
||||
|
||||
|
||||
class AlreadyRemovedRole(RoleAttributionError):
|
||||
pass
|
||||
|
||||
|
||||
class UnavailableRoleError(RoleAttributionError):
|
||||
pass
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "Roles"
|
||||
help = {
|
||||
"description": "Module gérant l'attribution des roles",
|
||||
"commands": {
|
||||
"`{prefix}{command} list`": "Liste les roles",
|
||||
"`{prefix}{command} add <role> [role] ...`": "S'attribuer le(s) rôle(s) <role> ([role]...)",
|
||||
"`{prefix}{command} remove <role> [role] ...`": "Se désattribuer le(s) rôle(s) <role> ([role]...)",
|
||||
"`{prefix}{command} toggle <role> [role] ...`": "S'attribuer (ou désattribuer) le(s) rôle(s) <role> ([role]...)",
|
||||
"`{prefix}{command} <role> [role] ...`": "Alias de `{prefix}{command} toggle`",
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.config.set({"roles": {}})
|
||||
|
||||
async def com_list(self, message, args, kwargs):
|
||||
response = discord.Embed(title="Roles disponibles", color=self.config.color)
|
||||
for id_ in self.config.roles.keys():
|
||||
role = message.guild.get_role(id_=int(id_))
|
||||
if role is not None:
|
||||
response.add_field(name=role.name, value=f"-> `{self.config.roles[id_]}`", inline=True)
|
||||
await message.channel.send(embed=response)
|
||||
|
||||
async def com_add(self, message, args, kwargs):
|
||||
if len(args) <= 1:
|
||||
await message.channel.send("Il manque des arguments à la commande")
|
||||
for role in args[1:]:
|
||||
try:
|
||||
await self.try_add_role(message.author, role)
|
||||
except discord.errors.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas la permission de modifier le role {role}.")
|
||||
except AlreadyHasRoleError:
|
||||
await message.channel.send(f"Vous avez déjà le role {role}.")
|
||||
except UnavailableRoleError:
|
||||
await message.channel.send(f"Le role {role} n'est pas une role disponible à l'autoattribution.")
|
||||
|
||||
async def com_remove(self, message, args, kwargs):
|
||||
if len(args) <= 1:
|
||||
await message.channel.send("Il manque des arguments à la commande")
|
||||
for role in args[1:]:
|
||||
try:
|
||||
await self.try_remove_role(message.author, role)
|
||||
except discord.errors.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas la permission de modifier le role {role}.")
|
||||
except AlreadyRemovedRole:
|
||||
await message.channel.send(f"Vous n'avez pas le role {role}.")
|
||||
except UnavailableRoleError:
|
||||
await message.channel.send(f"Le role {role} n'est pas une role disponible à l'autoattribution.")
|
||||
|
||||
async def com_toggle(self, message, args, kwargs):
|
||||
if len(args) <= 1:
|
||||
await message.channel.send("Il manque des arguments à la commande")
|
||||
for role in args[1:]:
|
||||
try:
|
||||
await self.try_toggle_role(message.author, role)
|
||||
except discord.errors.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas la permission de modifier le role {role}.")
|
||||
except AlreadyHasRoleError:
|
||||
await message.channel.send(f"Vous avez déjà le role {role}.")
|
||||
except AlreadyRemovedRole:
|
||||
await message.channel.send(f"Vous n'avez pas le role {role}.")
|
||||
except UnavailableRoleError:
|
||||
await message.channel.send(f"Le role {role} n'est pas une role disponible à l'autoattribution.")
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
if len(args) < 1:
|
||||
await message.channel.send("Il manque des arguments à la commande")
|
||||
for role in args:
|
||||
try:
|
||||
await self.try_toggle_role(message.author, role)
|
||||
except discord.errors.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas la permission de modifier le role {role}.")
|
||||
except AlreadyHasRoleError:
|
||||
await message.channel.send(f"Vous avez déjà le role {role}.")
|
||||
except AlreadyRemovedRole:
|
||||
await message.channel.send(f"Vous n'avez pas le role {role}.")
|
||||
except UnavailableRoleError:
|
||||
await message.channel.send(f"Le role {role} n'est pas une role disponible à l'autoattribution.")
|
||||
|
||||
def get_member(self, user):
|
||||
return self.client.get_guild(self.client.config.main_guild).get_member(user.id)
|
||||
|
||||
def get_role(self, role):
|
||||
role = self.client.id.get_role(name=role, guilds=[self.client.get_guild(self.client.config.main_guild)],
|
||||
check=lambda x: x.name.lower() == role.lower())
|
||||
if role is None or str(role.id) not in self.config.roles.keys():
|
||||
raise UnavailableRoleError()
|
||||
return role
|
||||
|
||||
async def try_toggle_role(self, user, role):
|
||||
if self.get_role(role) in self.get_member(user).roles:
|
||||
await self.try_remove_role(user, role)
|
||||
else:
|
||||
await self.try_add_role(user, role)
|
||||
|
||||
async def try_add_role(self, user, role):
|
||||
role = self.get_role(role)
|
||||
if role in user.roles:
|
||||
raise AlreadyHasRoleError()
|
||||
await self.get_member(user).add_roles(role, reason="Auto-attribution")
|
||||
|
||||
async def try_remove_role(self, user, role):
|
||||
role = self.get_role(role)
|
||||
if role not in user.roles:
|
||||
raise AlreadyRemovedRole()
|
||||
await self.get_member(user).remove_roles(role, reason="Auto-désattribution")
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import random
|
||||
|
||||
import discord
|
||||
|
||||
from modules.base import BaseClassPython
|
||||
|
||||
|
||||
class MainClass(BaseClassPython):
|
||||
name = "rtfgd"
|
||||
help = {
|
||||
"description": "Read the fucking google doc",
|
||||
"commands": {
|
||||
"{prefix}{command} <mention>": "Demande gentilment de lire le google doc"
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.config.set({"memes": []})
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
await message.channel.send(
|
||||
" ".join(member.mention for member in message.mentions),
|
||||
embed=discord.Embed(title="Read da fu**ing GOOGLE DOCS ! (╯°□°)╯︵ ┻━┻",
|
||||
color=self.config.color).set_image(url=random.choice(self.config.memes)))
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"type": "python",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
2
scripts/build-docs.sh
Normal file → Executable file
2
scripts/build-docs.sh
Normal file → Executable file
@ -1,2 +1,4 @@
|
||||
cd doc
|
||||
# Build html doc
|
||||
pipenv run make html
|
||||
cd ../
|
0
scripts/install-hooks.sh
Normal file → Executable file
0
scripts/install-hooks.sh
Normal file → Executable file
0
scripts/pre-commit.sh
Normal file → Executable file
0
scripts/pre-commit.sh
Normal file → Executable file
4
scripts/run-tests.sh
Normal file → Executable file
4
scripts/run-tests.sh
Normal file → Executable file
@ -4,10 +4,12 @@
|
||||
set -e
|
||||
|
||||
|
||||
cd "${0%/*}/.."
|
||||
cd "${0%/*}/../src"
|
||||
|
||||
|
||||
echo "Running tests"
|
||||
# Run test and ignore warnings
|
||||
pipenv run pytest -p no:warnings
|
||||
|
||||
cd "../"
|
||||
|
||||
|
1
src/bot_base/__init__.py
Normal file
1
src/bot_base/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
import bot_base
|
223
src/bot_base/bot_base.py
Normal file
223
src/bot_base/bot_base.py
Normal file
@ -0,0 +1,223 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import discord
|
||||
import toml
|
||||
from packaging.specifiers import SpecifierSet
|
||||
|
||||
from config import Config, config_types
|
||||
from config.config_types import factory
|
||||
import errors
|
||||
|
||||
__version__ = "0.1.0"
|
||||
MINIMAL_INFOS = ["version", "bot_version"]
|
||||
|
||||
|
||||
class BotBase(discord.Client):
|
||||
log = None
|
||||
|
||||
def __init__(self, data_folder: str = "data", modules_folder: str = "modules", *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Create folders
|
||||
os.makedirs(modules_folder, exist_ok=True)
|
||||
os.makedirs(data_folder, exist_ok=True)
|
||||
# Add module folder to search path
|
||||
# TODO: Vérifier que ca ne casse rien
|
||||
sys.path.insert(0, modules_folder)
|
||||
# Setup logging
|
||||
self.log = logging.getLogger('bot_base')
|
||||
# Content: {"module_name": {"module": imported module, "class": initialized class}}
|
||||
self.modules = {}
|
||||
|
||||
# Setup config
|
||||
self.config = Config(path=os.path.join(data_folder, "config.toml"))
|
||||
self.config.register("modules", factory(config_types.List, factory(config_types.Str)))
|
||||
self.config.register("prefix", factory(config_types.Str))
|
||||
self.config.register("admin_roles", factory(config_types.List, factory(config_types.discord_types.Role, self)))
|
||||
self.config.register("admin_users", factory(config_types.List, factory(config_types.discord_types.User, self)))
|
||||
self.config.register("main_guild", factory(config_types.discord_types.Guild, self))
|
||||
self.config.register("locale", factory(config_types.Str))
|
||||
self.config.register("data_folder", factory(config_types.Str))
|
||||
self.config.register("modules_folder", factory(config_types.Str))
|
||||
|
||||
self.config.set({
|
||||
"modules": [],
|
||||
"prefix": "%",
|
||||
"admin_roles": [],
|
||||
"admin_users": [],
|
||||
"main_guild": None,
|
||||
"locale": "fr_FR.UTF8",
|
||||
"data_folder": data_folder,
|
||||
"modules_folder": modules_folder,
|
||||
})
|
||||
|
||||
self.config.load()
|
||||
|
||||
async def on_ready(self):
|
||||
self.info("Bot ready.")
|
||||
try:
|
||||
self.load_modules()
|
||||
except errors.ModuleException as e:
|
||||
self.loop.stop()
|
||||
raise e
|
||||
|
||||
def load_modules(self):
|
||||
self.info("Load modules...")
|
||||
for module in self.config["modules"]:
|
||||
if module not in self.modules.keys():
|
||||
self.load_module(module)
|
||||
self.info("Modules loaded.")
|
||||
|
||||
def load_module(self, module: str) -> None:
|
||||
"""
|
||||
Try to load module
|
||||
|
||||
:raise ModuleNotFoundError: If module is not in module folder
|
||||
:raise IncompatibleModuleError: If module is incompatible
|
||||
:param str module: module to load
|
||||
"""
|
||||
self.info(f"Attempt to load module {module}...")
|
||||
# Check if module exists
|
||||
if not os.path.isdir(os.path.join(self.config["modules_folder"], module)):
|
||||
self.warning(f"Attempt to load unknown module {module}.")
|
||||
raise errors.ModuleNotFoundError(
|
||||
f"Module {module} not found in modules folder ({self.config['modules_folder']}.)")
|
||||
if not os.path.isfile(os.path.join(self.config["modules_folder"], module, "infos.toml")):
|
||||
self.warning(f"Attempt to load incompatible module {module}: no infos.toml found")
|
||||
raise errors.IncompatibleModuleError(f"Module {module} is incompatible: no infos.toml found.")
|
||||
# Check infos.toml integrity
|
||||
with open(os.path.join(self.config["modules_folder"], module, "infos.toml")) as f:
|
||||
infos = toml.load(f)
|
||||
for key in MINIMAL_INFOS:
|
||||
if key not in infos.keys():
|
||||
self.warning(f"Attempt to load incompatible module {module}: missing information {key}")
|
||||
raise errors.IncompatibleModuleError(f"Missing information for module {module}: missing {key}.")
|
||||
# Check bot_version
|
||||
bot_version_specifier = SpecifierSet(infos["bot_version"])
|
||||
if __version__ not in bot_version_specifier:
|
||||
self.warning(f"Attempt to load incompatible module {module}: need bot version {infos['bot_version']} "
|
||||
f"and you have {__version__}")
|
||||
raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current bot version "
|
||||
f"(need {infos['bot_version']} and you have {__version__}).")
|
||||
# Check dependencies
|
||||
if infos.get("dependencies"):
|
||||
for dep, version in infos["dependencies"].items():
|
||||
if not dep in self.modules.keys():
|
||||
self.load_module(dep)
|
||||
dep_version_specifier = SpecifierSet(version)
|
||||
if self.modules[dep]["infos"]["version"] not in dep_version_specifier:
|
||||
self.warning(f"Attempt to load incompatible module {module}: (require {dep} ({version}) "
|
||||
f"and you have {dep} ({self.modules[dep]['infos']['version']})")
|
||||
raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current install "
|
||||
f"(require {dep} ({version}) and you have {dep} "
|
||||
f"({self.modules[dep]['infos']['version']})")
|
||||
|
||||
# Check if module is meta
|
||||
if infos.get("metamodule", False) == False:
|
||||
# Check if module have __main_class__
|
||||
try:
|
||||
imported = importlib.import_module(module)
|
||||
except Exception as e:
|
||||
self.warning(f"Attempt to load incompatible module {module}: failed import")
|
||||
raise e
|
||||
try:
|
||||
main_class = imported.__main_class__
|
||||
except AttributeError:
|
||||
self.warning(f"Attempt to load incompatible module {module}: no __main_class__ found")
|
||||
raise errors.IncompatibleModuleError(f"Module {module} does not provide __main_class__.")
|
||||
# Check if __main_class__ is a class
|
||||
if not inspect.isclass(main_class):
|
||||
self.warning(f"Attempt to load incompatible module {module}: __main_class__ is not a type")
|
||||
raise errors.IncompatibleModuleError(f"Module {module} contains __main_class__ but it is not a type.")
|
||||
try:
|
||||
main_class = main_class(self)
|
||||
except TypeError:
|
||||
# Module don't need client reference
|
||||
main_class = main_class()
|
||||
# Check if __main_class__ have __dispatch__ attribute
|
||||
try:
|
||||
dispatch = main_class.__dispatch__
|
||||
except AttributeError:
|
||||
self.warning(f"Attempt to load incompatible module {module}: __dispatch_ not found")
|
||||
raise errors.IncompatibleModuleError(
|
||||
f"Module {module} mainclass ({main_class}) does not provide __dispatch__"
|
||||
f" attribute)")
|
||||
# Check if __dispatch__ is function
|
||||
if not inspect.isfunction(imported.__main_class__.__dispatch__):
|
||||
self.warning(f"Attempt to load incompatible module {module}: __dispatch__ is not a function")
|
||||
raise errors.IncompatibleModuleError(
|
||||
f"Module {module} mainclass ({main_class}) provides __dispatch__, but it is "
|
||||
f"not a function ({dispatch}).")
|
||||
# Check if __dispatch__ can have variable positional and keyword aguments (to avoid future error on each event)
|
||||
sig = inspect.signature(dispatch)
|
||||
args_present, kwargs_present = False, False
|
||||
for p in sig.parameters.values():
|
||||
if p.kind == p.VAR_POSITIONAL:
|
||||
args_present = True
|
||||
elif p.kind == p.VAR_KEYWORD:
|
||||
kwargs_present = True
|
||||
if not args_present:
|
||||
self.warning(f"Attempt to load incompatible module {module}: __dispatch__ doesn't accept variable "
|
||||
f"positional arguments")
|
||||
raise errors.IncompatibleModuleError(
|
||||
f"Module {module} mainclass ({main_class}) provide __dispatch__ function, but "
|
||||
f"this function doesn't accept variable positional arguments.")
|
||||
if not kwargs_present:
|
||||
self.warning(f"Attempt to load incompatible module {module}: __dispatch__ doesn't accept variable "
|
||||
f"keywords arguments.")
|
||||
raise errors.IncompatibleModuleError(
|
||||
f"Module {module} mainclass ({main_class}) provide __dispatch__ function, but "
|
||||
f"this function doesn't accept variable keywords arguments.")
|
||||
# Module is compatible!
|
||||
# Add module to loaded modules
|
||||
self.info(f"Add modules {module} to current modules.")
|
||||
self.modules.update({
|
||||
module: {
|
||||
"infos": infos,
|
||||
"imported": imported,
|
||||
"initialized_class": main_class,
|
||||
"dispatch": dispatch,
|
||||
}
|
||||
})
|
||||
else: # Module is metamodule
|
||||
self.info(f"Add modules {module} to current modules")
|
||||
self.modules.update({
|
||||
module: {
|
||||
"infos": infos,
|
||||
"dispatch": lambda *x, **y: None
|
||||
}
|
||||
})
|
||||
if module not in self.config["modules"]:
|
||||
self.config.set({"modules": self.config["modules"] + [module]})
|
||||
self.config.save()
|
||||
|
||||
def dispatch(self, event, *args, **kwargs):
|
||||
"""Dispatch event"""
|
||||
super().dispatch(event, *args, **kwargs)
|
||||
for module in self.modules.values():
|
||||
module["dispatch"](event, *args, **kwargs)
|
||||
|
||||
async def on_error(self, event_method, exc, *args, **kwargs):
|
||||
self.error(f"Error in {event_method}: \n{exc}")
|
||||
|
||||
# Logging
|
||||
def info(self, info, *args, **kwargs):
|
||||
if self.log:
|
||||
self.log.info(info, *args, **kwargs)
|
||||
self.dispatch("log_info", info, *args, **kwargs)
|
||||
|
||||
def error(self, e, *args, **kwargs):
|
||||
if self.log:
|
||||
self.log.error(e, *args, **kwargs)
|
||||
self.dispatch("log_error", e, *args, **kwargs)
|
||||
|
||||
def warning(self, warning, *args, **kwargs):
|
||||
if self.log:
|
||||
self.log.warning(warning, *args, **kwargs)
|
||||
self.dispatch("log_warning", warning, *args, **kwargs)
|
4
src/config/__init__.py
Normal file
4
src/config/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from . import config_types
|
||||
from .base import Config
|
||||
|
||||
__all__ = ["Config", "config_types"]
|
@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import typing
|
||||
|
||||
import toml
|
||||
@ -75,8 +76,9 @@ class Config:
|
||||
>>> config = Config("doctest_config.toml")
|
||||
>>> config.register("my_parameter", factory(Int))
|
||||
>>> config.set({"my_parameter": 3})
|
||||
>>> config.save()
|
||||
>>> config.save() #doctest: +SKIP
|
||||
"""
|
||||
os.makedirs(os.path.dirname(self.path), exist_ok=True)
|
||||
with open(self.path, 'w') as file:
|
||||
toml.dump({k: v.to_save() for k, v in self.fields.items()}, file)
|
||||
|
||||
@ -90,11 +92,11 @@ class Config:
|
||||
>>> config = Config("doctest_config.toml")
|
||||
>>> config.register("my_parameter", factory(Int))
|
||||
>>> config.set({"my_parameter": 3})
|
||||
>>> config.save()
|
||||
>>> config.save() #doctest: +SKIP
|
||||
>>> new_config = Config("doctest_config.toml")
|
||||
>>> new_config.register("my_parameter", factory(Int))
|
||||
>>> new_config.load()
|
||||
>>> new_config["my_parameter"]
|
||||
>>> new_config.load() #doctest: +SKIP
|
||||
>>> new_config["my_parameter"] #doctest: +SKIP
|
||||
3
|
||||
|
||||
:return: None
|
||||
@ -103,7 +105,8 @@ class Config:
|
||||
with open(self.path, 'r') as file:
|
||||
self.set(toml.load(file))
|
||||
except FileNotFoundError:
|
||||
self.save()
|
||||
pass
|
||||
self.save()
|
||||
|
||||
def __getitem__(self, item: str) -> typing.Any:
|
||||
"""
|
@ -1,16 +1,16 @@
|
||||
from typing import Type
|
||||
|
||||
import config.config_types.discord_types
|
||||
from config.config_types.base_type import BaseType
|
||||
from config.config_types.bool import Bool
|
||||
from config.config_types.color import Color
|
||||
from config.config_types.dict import Dict
|
||||
from config.config_types.float import Float
|
||||
from config.config_types.int import Int
|
||||
from config.config_types.list import List
|
||||
from config.config_types.str import Str
|
||||
from . import discord_types
|
||||
from .base_type import BaseType
|
||||
from .bool import Bool
|
||||
from .color import Color
|
||||
from .dict import Dict
|
||||
from .float import Float
|
||||
from .int import Int
|
||||
from .list import List
|
||||
from .str import Str
|
||||
|
||||
__all__ = ['factory', "BaseType", 'Dict', 'Float', 'Int', 'List', 'Str', 'discord_types', 'Bool', 'Color']
|
||||
__all__ = ['factory', 'Dict', 'Float', 'Int', 'List', 'Str', 'discord_types', 'Bool', 'Color']
|
||||
|
||||
|
||||
class Meta(type):
|
||||
@ -32,7 +32,7 @@ def factory(type: Type[BaseType], *args, **kwargs):
|
||||
>>> factory(Int, min=0, max=10)
|
||||
<config_types.Int with parameters () {'min': 0, 'max': 10}>
|
||||
|
||||
:param type: Type to create
|
||||
:param Type[BaseType] type: Type to create
|
||||
:return: New type
|
||||
"""
|
||||
|
@ -1,6 +1,6 @@
|
||||
import typing
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
from .base_type import BaseType
|
||||
|
||||
|
||||
class Bool(BaseType):
|
||||
@ -22,8 +22,6 @@ class Bool(BaseType):
|
||||
"""
|
||||
Check if value is a correct bool
|
||||
|
||||
Check if value is int, and if applicable, between ``min`` and ``max`` or in ``values``
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_bool = Bool()
|
||||
@ -74,7 +72,7 @@ class Bool(BaseType):
|
||||
:param bool value: Value to set
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
self.value = bool(value)
|
||||
|
||||
def get(self) -> typing.Optional[bool]:
|
||||
@ -122,7 +120,7 @@ class Bool(BaseType):
|
||||
:param bool value: Value to load
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
@ -1,6 +1,6 @@
|
||||
import typing
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
from .base_type import BaseType
|
||||
|
||||
|
||||
class Color(BaseType):
|
||||
@ -62,7 +62,7 @@ class Color(BaseType):
|
||||
:param int value: Value to set
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
self.value = int(value)
|
||||
|
||||
def get(self) -> typing.Optional[int]:
|
||||
@ -110,7 +110,7 @@ class Color(BaseType):
|
||||
:param int value: Value to load
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
@ -1,6 +1,6 @@
|
||||
import typing
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
from .base_type import BaseType
|
||||
|
||||
|
||||
class Dict(BaseType):
|
||||
@ -76,7 +76,7 @@ class Dict(BaseType):
|
||||
"""
|
||||
new_dict = dict()
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
for k, v in value.items():
|
||||
new_key = self.type_key()
|
||||
new_key.set(k)
|
@ -1,6 +1,6 @@
|
||||
from config.config_types.discord_types.channel import Channel
|
||||
from config.config_types.discord_types.guild import Guild
|
||||
from config.config_types.discord_types.user import User
|
||||
from config.config_types.discord_types.role import Role
|
||||
from config.config_types.discord_types.user import User
|
||||
|
||||
__all__ = ['Channel', "Guild", "User", "Role"]
|
155
src/config/config_types/discord_types/channel.py
Normal file
155
src/config/config_types/discord_types/channel.py
Normal file
@ -0,0 +1,155 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
|
||||
import discord
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from bot_base import BotBase
|
||||
|
||||
|
||||
class Channel(BaseType):
|
||||
#: :class:`BotBase`: Client instance for checking
|
||||
client: BotBase
|
||||
#: :class:`typing.Optional` [:class:`int`]: Current channel id
|
||||
value: int
|
||||
#: :class:`typing.Optional` [:class:`discord.TextChannel`]: Current channel instance
|
||||
channel_instance: typing.Optional[discord.TextChannel]
|
||||
|
||||
def __init__(self, client: BotBase) -> None:
|
||||
"""
|
||||
Base Channel type for config.
|
||||
|
||||
:param BotBase client: Client instance
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> Channel(client) #doctest: +SKIP
|
||||
<config_types.discord_type.Channel object with value None>
|
||||
"""
|
||||
self.value = 0
|
||||
self.channel_instance = None
|
||||
self.client = client
|
||||
|
||||
def check_value(self, value: typing.Union[int, discord.TextChannel]) -> bool:
|
||||
"""
|
||||
Check if value is correct
|
||||
|
||||
If bot is not connected, always True
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_channel = Channel(client) #doctest: +SKIP
|
||||
>>> my_channel.check_value(invalid_id_or_channel) #doctest: +SKIP
|
||||
False
|
||||
>>> my_channel.check_value(valid_id_or_channel) #doctest: +SKIP
|
||||
True
|
||||
|
||||
:param value: Value to test
|
||||
:type value: Union[int, discord.TextChannel]
|
||||
:return: True if channel exists
|
||||
"""
|
||||
id = value
|
||||
if isinstance(value, discord.TextChannel):
|
||||
id = value.id
|
||||
if not self.client.is_ready():
|
||||
self.client.warning(f"No check for channel {value} because client is not initialized!")
|
||||
return True
|
||||
if self.client.get_channel(id):
|
||||
return True
|
||||
return True
|
||||
|
||||
def set(self, value: typing.Union[int, discord.TextChannel]):
|
||||
"""
|
||||
Set value of parameter
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_channel = Channel(client) #doctest: +SKIP
|
||||
>>> my_channel.set(valid_id_or_channel) #doctest: +SKIP
|
||||
>>> my_channel.set(invalid_id_or_channel) #doctest: +SKIP +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: ...
|
||||
|
||||
:raise ValueError: if attempt to set invalid value
|
||||
:param value: value to set
|
||||
:type value: Union[int, discord.TextChannel]
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
if isinstance(value, discord.TextChannel):
|
||||
value = value.id
|
||||
self.value = value
|
||||
self._update()
|
||||
|
||||
def get(self) -> typing.Union[int, discord.Channel]:
|
||||
"""
|
||||
Get value of parameter
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_channel = Channel(client) #doctest: +SKIP
|
||||
>>> my_channel.set(valid_id_or_channel) #doctest: +SKIP
|
||||
>>> my_channel.get() #doctest: +SKIP
|
||||
<discord.channel.TextChannel at 0x...>
|
||||
|
||||
If client is not connected:
|
||||
>>> my_channel = Channel(client) #doctest: +SKIP
|
||||
>>> my_channel.set(valid_id_or_channel) #doctest: +SKIP
|
||||
>>> my_channel.get() #doctest: +SKIP
|
||||
23411424132412
|
||||
|
||||
:return: Channel object if client is connected, else id
|
||||
:rtype: Union[int, discord.Channel]
|
||||
"""
|
||||
if self.channel_instance is None:
|
||||
self._update()
|
||||
return self.channel_instance or self.value
|
||||
|
||||
def to_save(self) -> int:
|
||||
"""
|
||||
Return id of channel
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_channel = Channel(client) #doctest: +SKIP
|
||||
>>> my_channel.set(valid_id_or_channel) #doctest: +SKIP
|
||||
>>> my_channel.to_save() #doctest: +SKIP
|
||||
123412412421
|
||||
|
||||
:return: Current id
|
||||
:rtype: int
|
||||
"""
|
||||
return self.value or 0
|
||||
|
||||
def load(self, value: typing.Union[int, discord.Channel]) -> None:
|
||||
"""
|
||||
Load value from config
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_channel = Channel(client) #doctest: +SKIP
|
||||
>>> my_channel.set(valid_id) #doctest: +SKIP
|
||||
>>> my_channel.set(invalid_id) #doctest: +SKIP +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: ...
|
||||
|
||||
:raise ValueError: if attempt to set invalid value
|
||||
:param value: value to set
|
||||
:type value: Union[int, discord.TextChannel]
|
||||
"""
|
||||
if self.check_value(value):
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.set(value)
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
if self.client.is_ready() and self.channel_instance is None:
|
||||
self.channel_instance = self.client.get_channel(self.value)
|
||||
else:
|
||||
self.channel_instance = None
|
||||
|
||||
def __repr__(self):
|
||||
return f'<config_types.discord_types.Channel object with value {self.value}>'
|
@ -6,29 +6,30 @@ import discord
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
|
||||
LBI = typing.TypeVar('LBI')
|
||||
if typing.TYPE_CHECKING:
|
||||
from bot_base import BotBase
|
||||
|
||||
|
||||
class Guild(BaseType):
|
||||
#: :class:`LBI`: Client instance for checking
|
||||
client: LBI
|
||||
#: :class:`BotBase`: Client instance for checking
|
||||
client: BotBase
|
||||
#: :class:`typing.Optional` [:class:`int`]: Current guild id
|
||||
value: typing.Optional[int]
|
||||
#: :class:`typing.Optional` [:class:`discord.Guild`]: Current guild instance
|
||||
guild_instance: typing.Optional[discord.Guild]
|
||||
|
||||
def __init__(self, client: LBI) -> None:
|
||||
def __init__(self, client: BotBase) -> None:
|
||||
"""
|
||||
Base Guild type for config.
|
||||
|
||||
:param LBI client: Client instance
|
||||
:param BotBase client: Client instance
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> Guild(client) #doctest: +SKIP
|
||||
<config_types.discord_type.Guild object with value None>
|
||||
"""
|
||||
self.value = None
|
||||
self.value = 0
|
||||
self.guild_instance = None
|
||||
self.client = client
|
||||
|
||||
@ -55,13 +56,13 @@ class Guild(BaseType):
|
||||
if isinstance(value, discord.Guild):
|
||||
id = value.id
|
||||
if not self.client.is_ready():
|
||||
self.client.warn("No check for guild `value` because client is not initialized!")
|
||||
self.client.warning(f"No check for guild {value} because client is not initialized!")
|
||||
return True
|
||||
if self.client.get_guild(id):
|
||||
return True
|
||||
return True
|
||||
|
||||
def set(self, value: typing.Union[int, discord.Guild]):
|
||||
def set(self, value: typing.Union[int, discord.Guild]) -> None:
|
||||
"""
|
||||
Set value of parameter
|
||||
|
||||
@ -78,7 +79,9 @@ class Guild(BaseType):
|
||||
:type value: Union[int, discord.Guild]
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
if isinstance(value, discord.Guild):
|
||||
value = value.id
|
||||
self.value = value
|
||||
self._update()
|
||||
|
||||
@ -105,22 +108,23 @@ class Guild(BaseType):
|
||||
self._update()
|
||||
return self.guild_instance or self.value
|
||||
|
||||
def to_save(self) -> typing.Optional:
|
||||
def to_save(self) -> int:
|
||||
"""
|
||||
Return id of guild
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_guild = Guild(client) #doctest: +SKIP
|
||||
>>> my_guild.set(valid_id_or_guild) #doctest: +SKIP
|
||||
>>> my_guild.to_save() #doctest: +SKIP
|
||||
123412412421
|
||||
|
||||
:return: Current id
|
||||
:rtype: Optional[int]
|
||||
:rtype: int
|
||||
"""
|
||||
return self.value
|
||||
return self.value or 0
|
||||
|
||||
def load(self, value):
|
||||
def load(self, value: typing.Union[int, discord.Guild]) -> None:
|
||||
"""
|
||||
Load value from config
|
||||
|
||||
@ -137,11 +141,12 @@ class Guild(BaseType):
|
||||
:type value: Union[int, discord.Guild]
|
||||
"""
|
||||
if self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.set(value)
|
||||
self._update()
|
||||
|
||||
def __repr__(self):
|
||||
return f'<config_types.discord_types.guild object with value {self.value}>'
|
||||
return f'<config_types.discord_types.Guild object with value {self.value}>'
|
||||
|
||||
def _update(self):
|
||||
if self.client.is_ready() and self.guild_instance is None:
|
153
src/config/config_types/discord_types/role.py
Normal file
153
src/config/config_types/discord_types/role.py
Normal file
@ -0,0 +1,153 @@
|
||||
from __future__ import annotations
|
||||
import typing
|
||||
|
||||
import discord
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
if typing.TYPE_CHECKING:
|
||||
from bot_base import BotBase
|
||||
|
||||
|
||||
class Role(BaseType):
|
||||
#: :class:`BotBase`: Client instance for checking
|
||||
client: BotBase
|
||||
#: :class:`typing.Optional` [:class:`int`]: Current role id
|
||||
value: int
|
||||
#: :class:`typing.Optional` [:class:`discord.Role`]: Current role instance
|
||||
role_instance: typing.Optional[discord.Role]
|
||||
|
||||
def __init__(self, client: BotBase) -> None:
|
||||
"""
|
||||
Base Role type for config.
|
||||
|
||||
:param BotBase client: Client instance
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> Role(client) #doctest: +SKIP
|
||||
<config_types.discord_type.Role object with value None>
|
||||
"""
|
||||
self.value = 0
|
||||
self.role_instance = None
|
||||
self.client = client
|
||||
|
||||
def check_value(self, value: typing.Union[int, discord.Role]) -> bool:
|
||||
"""
|
||||
Check if value is correct
|
||||
|
||||
If bot is not connected, always True
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_role = Role(client) #doctest: +SKIP
|
||||
>>> my_role.check_value(invalid_id_or_role) #doctest: +SKIP
|
||||
False
|
||||
>>> my_role.check_value(valid_id_or_role) #doctest: +SKIP
|
||||
True
|
||||
|
||||
:param value: Value to test
|
||||
:type value: Union[int, discord.Role]
|
||||
:return: True if role exists
|
||||
"""
|
||||
id = value
|
||||
if isinstance(value, discord.Role):
|
||||
id = value.id
|
||||
if not self.client.is_ready():
|
||||
self.client.warning(f"No check for role {value} because client is not initialized!")
|
||||
return True
|
||||
if self.client.get_role(id):
|
||||
return True
|
||||
return True
|
||||
|
||||
def set(self, value: typing.Union[int, discord.Role]) -> None:
|
||||
"""
|
||||
Set value of parameter
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_role = Role(client) #doctest: +SKIP
|
||||
>>> my_role.set(valid_id_or_role) #doctest: +SKIP
|
||||
>>> my_role.set(invalid_id_or_role) #doctest: +SKIP +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: ...
|
||||
|
||||
:raise ValueError: if attempt to set invalid value
|
||||
:param value: value to set
|
||||
:type value: Union[int, discord.Role]
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
if isinstance(value, discord.Role):
|
||||
value = value.id
|
||||
self.value = value
|
||||
self._update()
|
||||
|
||||
def get(self) -> typing.Union[int, discord.Role]:
|
||||
"""
|
||||
Get value of parameter
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_role = Role(client) #doctest: +SKIP
|
||||
>>> my_role.set(valid_id_or_role) #doctest: +SKIP
|
||||
>>> my_role.get() #doctest: +SKIP
|
||||
<discord.role.Role at 0x...>
|
||||
|
||||
If client is not connected:
|
||||
>>> my_role = Role(client) #doctest: +SKIP
|
||||
>>> my_role.set(valid_id_or_role) #doctest: +SKIP
|
||||
>>> my_role.get() #doctest: +SKIP
|
||||
23411424132412
|
||||
|
||||
:return: Role object if client is connected, else id
|
||||
:rtype: Union[int, discord.Role]
|
||||
"""
|
||||
if self.role_instance is None:
|
||||
self._update()
|
||||
return self.role_instance or self.value
|
||||
|
||||
def to_save(self) -> int:
|
||||
"""
|
||||
Return id of role
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_role = Role(client) #doctest: +SKIP
|
||||
>>> my_role.set(valid_id_or_role) #doctest: +SKIP
|
||||
>>> my_role.to_save() #doctest: +SKIP
|
||||
123412412421
|
||||
|
||||
:return: Current id
|
||||
:rtype: int
|
||||
"""
|
||||
return self.value or 0
|
||||
|
||||
def load(self, value: typing.Union[int, discord.Role]) -> None:
|
||||
"""
|
||||
Load value from config
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_role = Role(client) #doctest: +SKIP
|
||||
>>> my_role.set(valid_id) #doctest: +SKIP
|
||||
>>> my_role.set(invalid_id) #doctest: +SKIP +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: ...
|
||||
|
||||
:raise ValueError: if attempt to set invalid value
|
||||
:param value: value to set
|
||||
:type value: Union[int, discord.Role]
|
||||
"""
|
||||
if self.check_value(value):
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.set(value)
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
if self.client.is_ready() and self.role_instance is None:
|
||||
self.role_instance = self.client.get_role(self.value)
|
||||
else:
|
||||
self.role_instance = None
|
||||
|
||||
def __repr__(self):
|
||||
return f'<config_types.discord_types.User object with value {self.value}>'
|
153
src/config/config_types/discord_types/user.py
Normal file
153
src/config/config_types/discord_types/user.py
Normal file
@ -0,0 +1,153 @@
|
||||
from __future__ import annotations
|
||||
import typing
|
||||
|
||||
import discord
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
if typing.TYPE_CHECKING:
|
||||
from bot_base import BotBase
|
||||
|
||||
|
||||
class User(BaseType):
|
||||
#: :class:`BotBase`: Client instance for checking
|
||||
client: BotBase
|
||||
#: :class:`typing.Optional` [:class:`int`]: Current user id
|
||||
value: int
|
||||
#: :class:`typing.Optional` [:class:`discord.User`]: Current user instance
|
||||
user_instance: typing.Optional[discord.User]
|
||||
|
||||
def __init__(self, client: BotBase) -> None:
|
||||
"""
|
||||
Base User type for config.
|
||||
|
||||
:param BotBase client: Client instance
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> User(client) #doctest: +SKIP
|
||||
<config_types.discord_type.User object with value None>
|
||||
"""
|
||||
self.value = 0
|
||||
self.user_instance = None
|
||||
self.client = client
|
||||
|
||||
def check_value(self, value: typing.Union[int, discord.User]) -> bool:
|
||||
"""
|
||||
Check if value is correct
|
||||
|
||||
If bot is not connected, always True
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_user = User(client) #doctest: +SKIP
|
||||
>>> my_user.check_value(invalid_id_or_user) #doctest: +SKIP
|
||||
False
|
||||
>>> my_user.check_value(valid_id_or_user) #doctest: +SKIP
|
||||
True
|
||||
|
||||
:param value: Value to test
|
||||
:type value: Union[int, discord.User]
|
||||
:return: True if user exists
|
||||
"""
|
||||
id = value
|
||||
if isinstance(value, discord.User):
|
||||
id = value.id
|
||||
if not self.client.is_ready():
|
||||
self.client.warning(f"No check for user {value} because client is not initialized!")
|
||||
return True
|
||||
if self.client.get_user(id):
|
||||
return True
|
||||
return True
|
||||
|
||||
def set(self, value: typing.Union[int, discord.User]) -> None:
|
||||
"""
|
||||
Set value of parameter
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_user = User(client) #doctest: +SKIP
|
||||
>>> my_user.set(valid_id_or_user) #doctest: +SKIP
|
||||
>>> my_user.set(invalid_id_or_user) #doctest: +SKIP +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: ...
|
||||
|
||||
:raise ValueError: if attempt to set invalid value
|
||||
:param value: value to set
|
||||
:type value: Union[int, discord.User]
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
if isinstance(value, discord.User):
|
||||
value = value.id
|
||||
self.value = value
|
||||
self._update()
|
||||
|
||||
def get(self) -> typing.Union[int, discord.User]:
|
||||
"""
|
||||
Get value of parameter
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_user = User(client) #doctest: +SKIP
|
||||
>>> my_user.set(valid_id_or_user) #doctest: +SKIP
|
||||
>>> my_user.get() #doctest: +SKIP
|
||||
<discord.user.User at 0x...>
|
||||
|
||||
If client is not connected:
|
||||
>>> my_user = User(client) #doctest: +SKIP
|
||||
>>> my_user.set(valid_id_or_user) #doctest: +SKIP
|
||||
>>> my_user.get() #doctest: +SKIP
|
||||
23411424132412
|
||||
|
||||
:return: User object if client is connected, else id
|
||||
:rtype: Union[int, discord.User]
|
||||
"""
|
||||
if self.user_instance is None:
|
||||
self._update()
|
||||
return self.user_instance or self.value
|
||||
|
||||
def to_save(self) -> int:
|
||||
"""
|
||||
Return id of user
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_user = User(client) #doctest: +SKIP
|
||||
>>> my_user.set(valid_id_or_user) #doctest: +SKIP
|
||||
>>> my_user.to_save() #doctest: +SKIP
|
||||
123412412421
|
||||
|
||||
:return: Current id
|
||||
:rtype: int
|
||||
"""
|
||||
return self.value or 0
|
||||
|
||||
def load(self, value: typing.Union[int, discord.User]) -> None:
|
||||
"""
|
||||
Load value from config
|
||||
|
||||
:Basic usage:
|
||||
|
||||
>>> my_user = User(client) #doctest: +SKIP
|
||||
>>> my_user.set(valid_id) #doctest: +SKIP
|
||||
>>> my_user.set(invalid_id) #doctest: +SKIP +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: ...
|
||||
|
||||
:raise ValueError: if attempt to set invalid value
|
||||
:param value: value to set
|
||||
:type value: Union[int, discord.User]
|
||||
"""
|
||||
if self.check_value(value):
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.set(value)
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
if self.client.is_ready() and self.user_instance is None:
|
||||
self.user_instance = self.client.get_user(self.value)
|
||||
else:
|
||||
self.user_instance = None
|
||||
|
||||
def __repr__(self):
|
||||
return f'<config_types.discord_types.User object with value {self.value}>'
|
@ -1,6 +1,6 @@
|
||||
import typing
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
from .base_type import BaseType
|
||||
|
||||
|
||||
class Float(BaseType):
|
||||
@ -98,7 +98,7 @@ class Float(BaseType):
|
||||
:param float value: Value to set
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
self.value = float(value)
|
||||
|
||||
def get(self) -> float:
|
||||
@ -146,7 +146,7 @@ class Float(BaseType):
|
||||
:param float value: Value to load
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
@ -1,6 +1,6 @@
|
||||
import typing
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
from .base_type import BaseType
|
||||
|
||||
|
||||
class Int(BaseType):
|
||||
@ -117,7 +117,7 @@ class Int(BaseType):
|
||||
:param int value: Value to set
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
self.value = int(value)
|
||||
|
||||
def get(self) -> typing.Optional[int]:
|
||||
@ -165,7 +165,7 @@ class Int(BaseType):
|
||||
:param int value: Value to load
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
@ -1,6 +1,6 @@
|
||||
import typing
|
||||
|
||||
from config.config_types.base_type import BaseType
|
||||
from .base_type import BaseType
|
||||
|
||||
|
||||
class List(BaseType):
|
||||
@ -70,7 +70,7 @@ class List(BaseType):
|
||||
:param typing.List[typing.Any] value: Value to set
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError('Tentative de définir une valeur incompatible')
|
||||
raise ValueError('Attempt to set incompatible value.')
|
||||
new_liste = []
|
||||
for v in value:
|
||||
new_element = self.type_()
|
||||
@ -128,7 +128,7 @@ class List(BaseType):
|
||||
:param typing.List[typing.Any] value: Value to load
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
for v in value:
|
||||
new_object = self.type_()
|
||||
new_object.load(v)
|
@ -1,4 +1,4 @@
|
||||
from config.config_types.base_type import BaseType
|
||||
from .base_type import BaseType
|
||||
|
||||
|
||||
class Str(BaseType):
|
||||
@ -52,7 +52,7 @@ class Str(BaseType):
|
||||
:return: None
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de définir une valeur incompatible")
|
||||
raise ValueError("Attempt to set incompatible value.")
|
||||
self.value = str(value)
|
||||
|
||||
def get(self) -> str:
|
||||
@ -103,7 +103,7 @@ class Str(BaseType):
|
||||
'34'
|
||||
"""
|
||||
if not self.check_value(value):
|
||||
raise ValueError("Tentative de charger une donnée incompatible.")
|
||||
raise ValueError("Attempt to load incompatible value.")
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
@ -41,7 +41,7 @@
|
||||
"level":"ERROR",
|
||||
"handlers":["console", "info_file_handler", "error_file_handler"]
|
||||
},
|
||||
"LBI": {
|
||||
"bot-base": {
|
||||
"level":"DEBUG",
|
||||
"handlers":["console", "info_file_handler", "error_file_handler"]
|
||||
}
|
14
src/errors.py
Normal file
14
src/errors.py
Normal file
@ -0,0 +1,14 @@
|
||||
class BotBaseException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ModuleException(BotBaseException):
|
||||
pass
|
||||
|
||||
|
||||
class ModuleNotFoundError(ModuleException):
|
||||
pass
|
||||
|
||||
|
||||
class IncompatibleModuleError(ModuleException):
|
||||
pass
|
35
src/main.py
Normal file
35
src/main.py
Normal file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/python3
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from bot_base.bot_base import BotBase
|
||||
|
||||
|
||||
def setup_logging(default_path='data/log_config.json', default_level=logging.INFO, env_key='BOT_BASE_LOG_CONFIG'):
|
||||
"""Setup logging configuration
|
||||
"""
|
||||
path = default_path
|
||||
value = os.getenv(env_key, None)
|
||||
if value:
|
||||
path = value
|
||||
if os.path.exists(path):
|
||||
with open(path, 'rt') as f:
|
||||
config = json.load(f)
|
||||
logging.config.dictConfig(config)
|
||||
else:
|
||||
logging.basicConfig(level=default_level)
|
||||
|
||||
|
||||
setup_logging()
|
||||
|
||||
if __name__ == "__main__":
|
||||
client = BotBase(max_messages=500000, data_folder="datas", modules_folder=os.environ.get("LOCAL_MODULES", "modules"))
|
||||
|
||||
async def start_bot():
|
||||
await client.start(os.environ.get("DISCORD_TOKEN"))
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.create_task(start_bot())
|
||||
loop.run_forever()
|
4
src/storage/__init__.py
Normal file
4
src/storage/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .jsonencoder import Encoder
|
||||
from .objects import Objects
|
||||
|
||||
__all__ = ["Objects", "Encoder"]
|
@ -1,7 +1,7 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from storage import jsonencoder
|
||||
from . import jsonencoder
|
||||
|
||||
|
||||
class Objects:
|
3
src/utils/__init__.py
Normal file
3
src/utils/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from . import emojis
|
||||
|
||||
__all__ = ["emojis"]
|
@ -1 +0,0 @@
|
||||
from .objects import Objects
|
Loading…
Reference in New Issue
Block a user