diff options
| author | 2019-03-05 21:03:41 -0700 | |
|---|---|---|
| committer | 2019-03-05 21:03:41 -0700 | |
| commit | 58a63aa27c49af02c5bddeeb00fcd409eef1b77d (patch) | |
| tree | 21e91a67533da26e8c87210a17e945963aa32606 | |
| parent | Update config-default.yml (diff) | |
Remove the "Snakes" cog
23 files changed, 228 insertions, 2380 deletions
| @@ -14,7 +14,6 @@ markdownify = "*"  lxml = "*"  pyyaml = "*"  fuzzywuzzy = "*" -pillow = "*"  aio-pika = "*"  python-dateutil = "*"  deepdiff = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 92566c3ed..20900c918 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@  {      "_meta": {          "hash": { -            "sha256": "d53f89c6d3b32ccbc2dadaff1a7e9ee1bdcbd1df9cddab35def36bcceec98b27" +            "sha256": "0cda63882f4a5ad3d210a90e76bde86fb18c32f2231023496c532a78b2a2c0cb"          },          "pipfile-spec": 6,          "requires": { @@ -18,19 +18,19 @@      "default": {          "aio-pika": {              "hashes": [ -                "sha256:c3eb639f7fc5c96355e7a227380989c9e0f342bb6612e6671ea76d188813ba45", -                "sha256:ea26efd262d7c4cd4ac00fb968ede89e82c00ad331b47415e3c2353a4b91cbe0" +                "sha256:22e722c7202d85abfbfbe9a9ee96fdf064d621489f45d4fd383306de5d765e12", +                "sha256:308b439822648c61634117d900678d71ef4a7afea7ce5b921826ce07b7f60290"              ],              "index": "pypi", -            "version": "==4.9.1" +            "version": "==5.3.2"          },          "aiodns": {              "hashes": [ -                "sha256:99d0652f2c02f73bfa646bf44af82705260a523014576647d7959e664830b26b", -                "sha256:d8677adc679ce8d0ef706c14d9c3d2f27a0e0cc11d59730cdbaf218ad52dd9ea" +                "sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d", +                "sha256:aaa5ac584f40fe778013df0aa6544bf157799bd3f608364b451840ed2c8688de"              ],              "index": "pypi", -            "version": "==1.1.1" +            "version": "==2.0.0"          },          "aiohttp": {              "hashes": [ @@ -60,6 +60,13 @@              "index": "pypi",              "version": "==3.4.4"          }, +        "aiormq": { +            "hashes": [ +                "sha256:40832b67a96b63d0dc70292daf094ec5a0cab8649120333543bd07ccafc2b502", +                "sha256:7d0146ea803699a5d4b7c1459c1065d110e52afcb785b2b9e70591a7af9ddbb2" +            ], +            "version": "==2.0.1" +        },          "alabaster": {              "hashes": [                  "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", @@ -76,10 +83,10 @@          },          "attrs": {              "hashes": [ -                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", -                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" +                "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", +                "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"              ], -            "version": "==18.2.0" +            "version": "==19.1.0"          },          "babel": {              "hashes": [ @@ -90,11 +97,11 @@          },          "beautifulsoup4": {              "hashes": [ -                "sha256:1ed70a0e99742653953d68462378a1a8eb65dca5f7c8fa44a05a2a0b3545df67", -                "sha256:6a7f5e0efc563cd1ffeefba6d528b97aa0d313c02dd126ba6c455e5fe5bd48eb", -                "sha256:e394827904cc4923f443e8dd2e9968343669c8e1ad7a8d62d7541e780884acb8" +                "sha256:034740f6cb549b4e932ae1ab975581e6103ac8f942200a0e9759065984391858", +                "sha256:945065979fb8529dd2f37dbb58f00b661bdbcbebf954f93b32fdf5263ef35348", +                "sha256:ba6d5c59906a85ac23dadfe5c88deaf3e179ef565f4898671253e50a78680718"              ], -            "version": "==4.7.0" +            "version": "==4.7.1"          },          "certifi": {              "hashes": [ @@ -105,40 +112,36 @@          },          "cffi": {              "hashes": [ -                "sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743", -                "sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef", -                "sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50", -                "sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f", -                "sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30", -                "sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93", -                "sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257", -                "sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b", -                "sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3", -                "sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e", -                "sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc", -                "sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04", -                "sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6", -                "sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359", -                "sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596", -                "sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b", -                "sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd", -                "sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95", -                "sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5", -                "sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e", -                "sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6", -                "sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca", -                "sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31", -                "sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1", -                "sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2", -                "sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085", -                "sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801", -                "sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4", -                "sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184", -                "sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917", -                "sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f", -                "sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb" -            ], -            "version": "==1.11.5" +                "sha256:00b97afa72c233495560a0793cdc86c2571721b4271c0667addc83c417f3d90f", +                "sha256:0ba1b0c90f2124459f6966a10c03794082a2f3985cd699d7d63c4a8dae113e11", +                "sha256:0bffb69da295a4fc3349f2ec7cbe16b8ba057b0a593a92cbe8396e535244ee9d", +                "sha256:21469a2b1082088d11ccd79dd84157ba42d940064abbfa59cf5f024c19cf4891", +                "sha256:2e4812f7fa984bf1ab253a40f1f4391b604f7fc424a3e21f7de542a7f8f7aedf", +                "sha256:2eac2cdd07b9049dd4e68449b90d3ef1adc7c759463af5beb53a84f1db62e36c", +                "sha256:2f9089979d7456c74d21303c7851f158833d48fb265876923edcb2d0194104ed", +                "sha256:3dd13feff00bddb0bd2d650cdb7338f815c1789a91a6f68fdc00e5c5ed40329b", +                "sha256:4065c32b52f4b142f417af6f33a5024edc1336aa845b9d5a8d86071f6fcaac5a", +                "sha256:51a4ba1256e9003a3acf508e3b4f4661bebd015b8180cc31849da222426ef585", +                "sha256:59888faac06403767c0cf8cfb3f4a777b2939b1fbd9f729299b5384f097f05ea", +                "sha256:59c87886640574d8b14910840327f5cd15954e26ed0bbd4e7cef95fa5aef218f", +                "sha256:610fc7d6db6c56a244c2701575f6851461753c60f73f2de89c79bbf1cc807f33", +                "sha256:70aeadeecb281ea901bf4230c6222af0248c41044d6f57401a614ea59d96d145", +                "sha256:71e1296d5e66c59cd2c0f2d72dc476d42afe02aeddc833d8e05630a0551dad7a", +                "sha256:8fc7a49b440ea752cfdf1d51a586fd08d395ff7a5d555dc69e84b1939f7ddee3", +                "sha256:9b5c2afd2d6e3771d516045a6cfa11a8da9a60e3d128746a7fe9ab36dfe7221f", +                "sha256:9c759051ebcb244d9d55ee791259ddd158188d15adee3c152502d3b69005e6bd", +                "sha256:b4d1011fec5ec12aa7cc10c05a2f2f12dfa0adfe958e56ae38dc140614035804", +                "sha256:b4f1d6332339ecc61275bebd1f7b674098a66fea11a00c84d1c58851e618dc0d", +                "sha256:c030cda3dc8e62b814831faa4eb93dd9a46498af8cd1d5c178c2de856972fd92", +                "sha256:c2e1f2012e56d61390c0e668c20c4fb0ae667c44d6f6a2eeea5d7148dcd3df9f", +                "sha256:c37c77d6562074452120fc6c02ad86ec928f5710fbc435a181d69334b4de1d84", +                "sha256:c8149780c60f8fd02752d0429246088c6c04e234b895c4a42e1ea9b4de8d27fb", +                "sha256:cbeeef1dc3c4299bd746b774f019de9e4672f7cc666c777cd5b409f0b746dac7", +                "sha256:e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7", +                "sha256:e21162bf941b85c0cda08224dade5def9360f53b09f9f259adb85fc7dd0e7b35", +                "sha256:fb6934ef4744becbda3143d30c6604718871495a5e36c408431bf33d9c146889" +            ], +            "version": "==1.12.2"          },          "chardet": {              "hashes": [ @@ -174,10 +177,10 @@          },          "dulwich": {              "hashes": [ -                "sha256:5e1e39555f594939a8aff1ca08b3bdf6c7efd4b941c2850760983a0197240974" +                "sha256:afbe070f6899357e33f63f3f3696e601731fef66c64a489dea1bc9f539f4a725"              ],              "index": "pypi", -            "version": "==0.19.9" +            "version": "==0.19.11"          },          "fuzzywuzzy": {              "hashes": [ @@ -194,6 +197,13 @@              ],              "version": "==2.8"          }, +        "idna-ssl": { +            "hashes": [ +                "sha256:a933e3bb13da54383f9e8f35dc4f9cb9eb9b3b78c6b36f311254d6d0d92c6c7c" +            ], +            "markers": "python_version < '3.7'", +            "version": "==1.1.0" +        },          "imagesize": {              "hashes": [                  "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", @@ -210,11 +220,10 @@          },          "jsonpickle": {              "hashes": [ -                "sha256:8b6212f1155f43ce67fa945efae6d010ed059f3ca5ed377aa070e5903d45b722", -                "sha256:d43ede55b3d9b5524a8e11566ea0b11c9c8109116ef6a509a1b619d2041e7397", -                "sha256:ed4adf0d14564c56023862eabfac211cf01211a20c5271896c8ab6f80c68086c" +                "sha256:0231d6f7ebc4723169310141352d9c9b7bbbd6f3be110cf634575d2bf2af91f0", +                "sha256:625098cc8e5854b8c23b587aec33bc8e33e0e597636bfaca76152249c78fe5c1"              ], -            "version": "==1.0" +            "version": "==1.1"          },          "logmatic-python": {              "hashes": [ @@ -225,39 +234,35 @@          },          "lxml": {              "hashes": [ -                "sha256:16cf8bac33ec17049617186d63006ba49da7c5be417042877a49f0ef6d7a195d", -                "sha256:18f2d8f14cc61e66e8a45f740d15b6fc683c096f733db1f8d0ee15bcac9843de", -                "sha256:260868f69d14a64dd1de9cf92e133d2f71514d288de4906f109bdf48ca9b756a", -                "sha256:29b8acd8ecdf772266dbac491f203c71664b0b07ad4309ba2c3bb131306332fc", -                "sha256:2b05e5e06f8e8c63595472dc887d0d6e0250af754a35ba690f6a6abf2ef85691", -                "sha256:30d6ec05fb607a5b7345549f642c7c7a5b747b634f6d5e935596b910f243f96f", -                "sha256:3bf683f0237449ebc1851098f664410e3c99ba3faa8c9cc82c6acfe857df1767", -                "sha256:3ce5488121eb15513c4b239dadd67f9e7959511bd766aac6be0c35e80274f298", -                "sha256:48be0c375350a5519bb9474b42a9c0e7ab709fb45f11bfcd33de876791137896", -                "sha256:49bc343ca3b30cd860845433bb9f62448a54ff87b632175108bacbc5dc63e49e", -                "sha256:4cc7531e86a43ea66601763c5914c3d3adb297f32e4284957609b90d41825fca", -                "sha256:4e9822fad564d82035f0b6d701a890444560210f8a8648b8f15850f8fe883cd9", -                "sha256:51a9a441aefc8c93512bad5efe867d2ff086e7249ce0fc3b47c310644b352936", -                "sha256:5bbed9efc8aeb69929140f71a30e655bf496b45b766861513960e1b11168d475", -                "sha256:60a5323b2bc893ca1059d283d6695a172d51cc95a70c25b3e587e1aad5459c38", -                "sha256:7035d9361f3ceec9ccc1dd3482094d1174580e7e1bf6870b77ea758f7cad15d2", -                "sha256:76d62cc048bda0ebf476689ad3eb8e65e6827e43a7521be3b163071020667b8c", -                "sha256:78163b578e6d1836012febaa1865e095ccc7fc826964dd69a2dbfe401618a1f7", -                "sha256:83b58b2b5904d50de03a47e2f56d24e9da4cf7e3b0d66fb4510b18fca0faf910", -                "sha256:a07447e46fffa5bb4d7a0af0a6505c8517e9bd197cfd2aec79e499b6e86cde49", -                "sha256:a17d808b3edca4aaf6b295b5a388c844a0b7f79aca2d79eec5acc1461db739e3", -                "sha256:a378fd61022cf4d3b492134c3bc48204ac2ff19e0813b23e07c3dd95ae8df0bc", -                "sha256:aa7d096a44ae3d475c5ed763e24cf302d32462e78b61bba73ce1ad0efb8f522a", -                "sha256:ade8785c93a985956ba6499d5ea6d0a362e24b4a9ba07dd18920fd67cccf63ea", -                "sha256:cc039668f91d8af8c4094cfb5a67c7ae733967fdc84c0507fe271db81480d367", -                "sha256:d89f1ffe98744c4b5c11f00fb843a4e72f68a6279b5e38168167f1b3c0fdd84c", -                "sha256:e691b6ef6e27437860016bd6c32e481bdc2ed3af03289707a38b9ca422105f40", -                "sha256:e750da6ac3ca624ae3303df448664012f9b6f9dfbc5d50048ea8a12ce2f8bc29", -                "sha256:eca305b200549906ea25648463aeb1b3b220b716415183eaa99c998a846936d9", -                "sha256:f52fe795e08858192eea167290033b5ff24f50f51781cb78d989e8d63cfe73d1" +                "sha256:0358b9e9642bc7d39aac5cffe9884a99a5ca68e5e2c1b89e570ed60da9139908", +                "sha256:091a359c4dafebbecd3959d9013f1b896b5371859165e4e50b01607a98d9e3e2", +                "sha256:1998e4e60603c64bcc35af61b4331ab3af087457900d3980e18d190e17c3a697", +                "sha256:2000b4088dee9a41f459fddaf6609bba48a435ce6374bb254c5ccdaa8928c5ba", +                "sha256:2afb0064780d8aaf165875be5898c1866766e56175714fa5f9d055433e92d41d", +                "sha256:2d8f1d9334a4e3ff176d096c14ded3100547d73440683567d85b8842a53180bb", +                "sha256:2e38db22f6a3199fd63675e1b4bd795d676d906869047398f29f38ca55cb453a", +                "sha256:3181f84649c1a1ca62b19ddf28436b1b2cb05ae6c7d2628f33872e713994c364", +                "sha256:37462170dfd88af8431d04de6b236e6e9c06cda71e2ca26d88ef2332fd2a5237", +                "sha256:3a9d8521c89bf6f2a929c3d12ad3ad7392c774c327ea809fd08a13be6b3bc05f", +                "sha256:3d0bbd2e1a28b4429f24fd63a122a450ce9edb7a8063d070790092d7343a1aa4", +                "sha256:483d60585ce3ee71929cea70949059f83850fa5e12deb9c094ed1c8c2ec73cbd", +                "sha256:4888be27d5cba55ce94209baef5bcd7bbd7314a3d17021a5fc10000b3a5f737d", +                "sha256:64b0d62e4209170a2a0c404c446ab83b941a0003e96604d2e4f4cb735f8a2254", +                "sha256:68010900898fdf139ac08549c4dba8206c584070a960ffc530aebf0c6f2794ef", +                "sha256:872ecb066de602a0099db98bd9e57f4cfc1d62f6093d94460c787737aa08f39e", +                "sha256:88a32b03f2e4cd0e63f154cac76724709f40b3fc2f30139eb5d6f900521b44ed", +                "sha256:b1dc7683da4e67ab2bebf266afa68098d681ae02ce570f0d1117312273d2b2ac", +                "sha256:b29e27ce9371810250cb1528a771d047a9c7b0f79630dc7dc5815ff828f4273b", +                "sha256:ce197559596370d985f1ce6b7051b52126849d8159040293bf8b98cb2b3e1f78", +                "sha256:d45cf6daaf22584eff2175f48f82c4aa24d8e72a44913c5aff801819bb73d11f", +                "sha256:e2ff9496322b2ce947ba4a7a5eb048158de9d6f3fe9efce29f1e8dd6878561e6", +                "sha256:f7b979518ec1f294a41a707c007d54d0f3b3e1fd15d5b26b7e99b62b10d9a72e", +                "sha256:f9c7268e9d16e34e50f8246c4f24cf7353764affd2bc971f0379514c246e3f6b", +                "sha256:f9c839806089d79de588ee1dde2dae05dc1156d3355dfeb2b51fde84d9c960ad", +                "sha256:ff962953e2389226adc4d355e34a98b0b800984399153c6678f2367b11b4d4b8"              ],              "index": "pypi", -            "version": "==4.2.6" +            "version": "==4.3.2"          },          "markdownify": {              "hashes": [ @@ -268,36 +273,36 @@          },          "markupsafe": {              "hashes": [ -                "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", -                "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", -                "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", -                "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", -                "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", -                "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", -                "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", -                "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", -                "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", -                "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", -                "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", -                "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", -                "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", -                "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", -                "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", -                "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", -                "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", -                "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", -                "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", -                "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", -                "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", -                "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", -                "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", -                "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", -                "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", -                "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", -                "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", -                "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1" +                "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", +                "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", +                "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", +                "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", +                "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", +                "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", +                "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", +                "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", +                "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", +                "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", +                "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", +                "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", +                "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", +                "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", +                "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", +                "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", +                "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", +                "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", +                "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", +                "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", +                "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", +                "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", +                "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", +                "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", +                "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", +                "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", +                "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", +                "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"              ], -            "version": "==1.1.0" +            "version": "==1.1.1"          },          "multidict": {              "hashes": [ @@ -335,79 +340,40 @@          },          "packaging": {              "hashes": [ -                "sha256:0886227f54515e592aaa2e5a553332c73962917f2831f1b0f9b9f4380a4b9807", -                "sha256:f95a1e147590f204328170981833854229bb2912ac3d5f89e2a8ccd2834800c9" -            ], -            "version": "==18.0" -        }, -        "pillow": { -            "hashes": [ -                "sha256:0cd42fe2d99ec6ce23aaf00947a7b7956ad2ed4b1695fd37545c3b8eae06d95a", -                "sha256:137bed8972089d65da63fb79b4949b0f2b99e9a58f1b494e82be43ba8b0f4226", -                "sha256:14eb2b2e4f2a14f5c89fd0edf55c5af0bf1a40fdf3838d81867f26f131cd557d", -                "sha256:1fc43ce8c4fa3754222cd6831d599ad17ca2fc9868d2fb52f4e5362dfbfaf379", -                "sha256:26dfeee23a86dad6277a63d18f61f53b957cb2cd3506dbbd74b88ba2fa65b3b1", -                "sha256:2e0e582942e025cc58f669499a8e0bffde5bcc8d42b65729f294c1dac54e4672", -                "sha256:3bb8dd3ce101dd8b0b37eaae924a5bb93abb6ffdd034bf68a066a808e11768ab", -                "sha256:3f07da3874f0b085421f1d4f979785131aa9d497501d8610d82f7378b33858f8", -                "sha256:429b2b5ae5f57f8fd9ec2e012c1e7b342ff10f1a8977dc291976b9a3b4c096e1", -                "sha256:4a000fdd89d77b6b675de27e1ab91c6fba517c08f19ee83e6716b78930634e04", -                "sha256:4ccbe7cce6156391a3ecf447c79a7d4a1a0ecd3de79bdec9ca5e4f7242a306d1", -                "sha256:4d08034196db41acb7392e4fccfc0448e7a87192c41d3011ad4093eac2c31ffd", -                "sha256:6b202b1cb524bc76ed52a7eb0314f4b0a0497c7cceb9a93539b5a25800e1f2b6", -                "sha256:8563b56fa7c34f1606848c2143ea67d27cf225b9726a1b041c3d27cf85e46edc", -                "sha256:86d7421e8803d7bae2e594765c378a867b629d46b32fbfe5ed9fd95b30989feb", -                "sha256:8d4bddedcb4ab99131d9705a75720efc48b3d006122dae1a4cc329496ac47c9a", -                "sha256:a4929c6de9590635c34533609402c9da12b22bfc2feb8c0c4f38c39bab48a9ad", -                "sha256:b0736e21798448cee3e663c0df7a6dfa83d805b3f3a45e67f7457a2f019e5fca", -                "sha256:b669acba91d47395de84c9ca52a7ad393b487e5ae2e20b9b2790b22a57d479fa", -                "sha256:bba993443921f2d077195b425a3283357f52b07807d53704610c1249d20b183a", -                "sha256:bdf706a93d00547c9443b2654ae424fd54d5dece4bc4333e7035740aeb7a7cea", -                "sha256:c5aa93e55175b9cde95279ccd03c93d218976b376480222d37be41d2c9c54510", -                "sha256:cc11fd997d8ad71bb0412e983b711e49639c2ddba9b9dce04d4bdab575fe5f84", -                "sha256:d584f1c33995c3dc16a35e30ef43e0881fa0d085f0fef29cebf154ffb5643363", -                "sha256:d88f54bdefb7ddccb68efdd710d689aa6a09b875cc3e44b7e81ef54e0751e3a7", -                "sha256:de0d323072be72fa4d74f4e013cd594e3f8ee03b2e0eac5876a3249fa076ef7b", -                "sha256:f139c963c6679d236b2c45369524338eabd36a853fe23abd39ba246ab0a75aec", -                "sha256:f41c0bf667c4c1c30b873eaa8d6bb894f6d721b3e38e9c993bddd1263c02fb1f", -                "sha256:fbd0ea468b4ec04270533bf5206f1cd57746fcf226520bb133318fa276de2644", -                "sha256:fe2d2850521c467c915ff0a6e27dc64c3c04c2f66612e0072672bd1bd4854b61" +                "sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", +                "sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"              ], -            "index": "pypi", -            "version": "==5.4.0" +            "version": "==19.0" +        }, +        "pamqp": { +            "hashes": [ +                "sha256:24370038a22583b8036fe92f94e5d04347e2b0f8e2502513dc5741472387b13e", +                "sha256:e1fa1107a195993fca6e04f1eb7286b60e223c958944d7808a501258ccc0ef8c" +            ], +            "version": "==2.1.0"          },          "pycares": {              "hashes": [ -                "sha256:080ae0f1b1b754be60b6ef31b9ab2915364c210eb1cb4d8e089357c89d7b9819", -                "sha256:0eccb76dff0155ddf793a589c6270e1bdbf6975b2824d18d1d23db2075d7fc96", -                "sha256:223a03d69e864a18d7bb2e0108bca5ba069ef91e5b048b953ed90ea9f50eb77f", -                "sha256:289e49f98adfd7a2ae3656df26e1d62cf49a06bbc03ced63f243c22cd8919adf", -                "sha256:292ac442a1d4ff27d41be748ec19f0c4ff47efebfb715064ba336564ea0f2071", -                "sha256:34771095123da0e54597fe3c5585a28d3799945257e51b378a20778bf33573b6", -                "sha256:34c8865f2d047be4c301ce90a916c7748be597e271c5c7932e8b9a6de85840f4", -                "sha256:36af260b215f86ebfe4a5e4aea82fd6036168a5710cbf8aad77019ab52156dda", -                "sha256:5e8e2a461717da40482b5fecf1119116234922d29660b3c3e01cbc5ba2cbf4bd", -                "sha256:61e77bd75542c56dff49434fedbafb25604997bc57dc0ebf791a5732503cb1bb", -                "sha256:691740c332f38a9035b4c6d1f0e6c8af239466ef2373a894d4393f0ea65c815d", -                "sha256:6bc0e0fdcb4cdc4ca06aa0b07e6e3560d62b2af79ef0ea4589835fcd2059012b", -                "sha256:96db5c93e2fe2e39f519efb7bb9d86aef56f5813fa0b032e47aba329fa925d57", -                "sha256:af701b22c91b3e36f65ee9f4b1bc2fe4800c8ed486eb6ef203624acbe53d026d", -                "sha256:b25bd21bba9c43d44320b719118c2ce35e4a78031f61d906caeb01316d49dafb", -                "sha256:c42f68319f8ea2322ed81c31a86c4e60547e6e90f3ebef479a7a7540bddbf268", -                "sha256:cc9a8d35af12bc5f484f3496f9cb3ab5bedfa4dcf3dfff953099453d88b659a7", -                "sha256:dfee9d198ba6d6f29aa5bf510bfb2c28a60c3f308116f114c9fd311980d3e870", -                "sha256:e1dd02e110a7a97582097ebba6713d9da28583b538c08e8a14bc82169c5d3e10", -                "sha256:e48c586c80a139c6c7fb0298b944d1c40752cf839bc8584cc793e42a8971ba6c", -                "sha256:f509762dec1a70eac32b86c098f37ac9c5d3d4a8a9098983328377c9e71543b2", -                "sha256:f8e0d61733843844f9019c911d5676818d99c4cd2c54b91de58384c7d962862b", -                "sha256:fe20280fed496deba60e0f6437b7672bdc83bf45e243bb546af47c60c85bcfbc" -            ], -            "version": "==2.4.0" +                "sha256:2ca080db265ea238dc45f997f94effb62b979a617569889e265c26a839ed6305", +                "sha256:6f79c6afb6ce603009db2042fddc2e348ad093ece9784cbe2daa809499871a23", +                "sha256:70918d06eb0603016d37092a5f2c0228509eb4e6c5a3faacb4184f6ab7be7650", +                "sha256:755187d28d24a9ea63aa2b4c0638be31d65fbf7f0ce16d41261b9f8cb55a1b99", +                "sha256:7baa4b1f2146eb8423ff8303ebde3a20fb444a60db761fba0430d104fe35ddbf", +                "sha256:90b27d4df86395f465a171386bc341098d6d47b65944df46518814ae298f6cc6", +                "sha256:9e090dd6b2afa65cb51c133883b2bf2240fd0f717b130b0048714b33fb0f47ce", +                "sha256:a11b7d63c3718775f6e805d6464cb10943780395ab042c7e5a0a7a9f612735dd", +                "sha256:b253f5dcaa0ac7076b79388a3ac80dd8f3bd979108f813baade40d3a9b8bf0bd", +                "sha256:c7f4f65e44ba35e35ad3febc844270665bba21cfb0fb7d749434e705b556e087", +                "sha256:cdb342e6a254f035bd976d95807a2184038fc088d957a5104dcaab8be602c093", +                "sha256:cf08e164f8bfb83b9fe633feb56f2754fae6baefcea663593794fa0518f8f98c", +                "sha256:df9bc694cf03673878ea8ce674082c5acd134991d64d6c306d4bd61c0c1df98f" +            ], +            "version": "==3.0.0"          },          "pycparser": {              "hashes": [ -                "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3", -                "sha256:db32bd592ba104f8fbb8047c18cd897f0f20d0909ba0ec5dc72a3221f6a82e15" +                "sha256:2d4f907c7667f7a264d65e5f48a516c70ea07e6ecb6f121588c1c97cd536a6b5", +                "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"              ],              "version": "==2.19"          }, @@ -456,18 +422,18 @@          },          "pyparsing": {              "hashes": [ -                "sha256:40856e74d4987de5d01761a22d1621ae1c7f8774585acae358aa5c5936c6c90b", -                "sha256:f353aab21fd474459d97b709e527b5571314ee5f067441dc9f88e33eecd96592" +                "sha256:66c9268862641abcac4a96ba74506e594c884e3f57690a696d21ad8210ed667a", +                "sha256:f6c5ef0d7480ad048c054c37632c67fca55299990fff127850181659eea33fc3"              ], -            "version": "==2.3.0" +            "version": "==2.3.1"          },          "python-dateutil": {              "hashes": [ -                "sha256:063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93", -                "sha256:88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02" +                "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", +                "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"              ],              "index": "pypi", -            "version": "==2.7.5" +            "version": "==2.8.0"          },          "python-json-logger": {              "hashes": [ @@ -478,10 +444,10 @@          },          "pytz": {              "hashes": [ -                "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca", -                "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6" +                "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", +                "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"              ], -            "version": "==2018.7" +            "version": "==2018.9"          },          "pyyaml": {              "hashes": [ @@ -508,12 +474,6 @@              "index": "pypi",              "version": "==2.21.0"          }, -        "shortuuid": { -            "hashes": [ -                "sha256:d08fd398f40f8baf87e15eef8355e92fa541bca4eb8465fefab7ee22f92711b9" -            ], -            "version": "==0.5.0" -        },          "six": {              "hashes": [                  "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", @@ -530,18 +490,18 @@          },          "soupsieve": {              "hashes": [ -                "sha256:057e08f362a255b457a5781675211556799ed3bb8807506eaac3809390bc304b", -                "sha256:f7d99b41637be2f249dfcc06ae93c13fcbbdfa7bb68b15308cdd0734e58146f1" +                "sha256:afa56bf14907bb09403e5d15fbed6275caa4174d36b975226e3b67a3bb6e2c4b", +                "sha256:eaed742b48b1f3e2d45ba6f79401b2ed5dc33b2123dfe216adb90d4bfa0ade26"              ], -            "version": "==1.6.1" +            "version": "==1.8"          },          "sphinx": {              "hashes": [ -                "sha256:429e3172466df289f0f742471d7e30ba3ee11f3b5aecd9a840480d03f14bcfe5", -                "sha256:c4cb17ba44acffae3d3209646b6baec1e215cad3065e852c68cc569d4df1b9f8" +                "sha256:b53904fa7cb4b06a39409a492b949193a1b68cc7241a1a8ce9974f86f0d24287", +                "sha256:c1c00fc4f6e8b101a0d037065043460dffc2d507257f2f11acaed71fd2b0c83c"              ],              "index": "pypi", -            "version": "==1.8.3" +            "version": "==1.8.4"          },          "sphinxcontrib-websupport": {              "hashes": [ @@ -550,6 +510,15 @@              ],              "version": "==1.1.0"          }, +        "typing": { +            "hashes": [ +                "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", +                "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", +                "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a" +            ], +            "markers": "python_version < '3.7'", +            "version": "==3.6.6" +        },          "urllib3": {              "hashes": [                  "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", @@ -603,24 +572,17 @@      "develop": {          "aspy.yaml": {              "hashes": [ -                "sha256:04d26279513618f1024e1aba46471db870b3b33aef204c2d09bcf93bea9ba13f", -                "sha256:0a77e23fafe7b242068ffc0252cee130d3e509040908fc678d9d1060e7494baa" +                "sha256:ae249074803e8b957c83fdd82a99160d0d6d26dff9ba81ba608b42eebd7d8cd3", +                "sha256:c7390d79f58eb9157406966201abf26da0d56c07e0ff0deadc39c8f4dbc13482"              ], -            "version": "==1.1.1" +            "version": "==1.2.0"          },          "attrs": {              "hashes": [ -                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", -                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" -            ], -            "version": "==18.2.0" -        }, -        "cached-property": { -            "hashes": [ -                "sha256:3a026f1a54135677e7da5ce819b0c690f156f37976f3e30c5430740725203d7f", -                "sha256:9217a59f14a5682da7c4b8829deadbfc194ac22e9908ccf7c8820234e80a1504" +                "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", +                "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"              ], -            "version": "==1.5.1" +            "version": "==19.1.0"          },          "certifi": {              "hashes": [ @@ -631,10 +593,10 @@          },          "cfgv": {              "hashes": [ -                "sha256:73f48a752bd7aab103c4b882d6596c6360b7aa63b34073dd2c35c7b4b8f93010", -                "sha256:d1791caa9ff5c0c7bce80e7ecc1921752a2eb7c2463a08ed9b6c96b85a2f75aa" +                "sha256:39f8475d8eca48639f900daffa3f8bd2f60a31d989df41a9f81c5ad1779a66eb", +                "sha256:a6a4366d32799a6bfb6f577ebe113b27ba8d1bae43cb57133b1472c1c3dae227"              ], -            "version": "==1.1.0" +            "version": "==1.5.0"          },          "chardet": {              "hashes": [ @@ -664,13 +626,20 @@              ],              "version": "==0.4.1"          }, +        "entrypoints": { +            "hashes": [ +                "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", +                "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451" +            ], +            "version": "==0.3" +        },          "flake8": {              "hashes": [ -                "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670", -                "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2" +                "sha256:859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661", +                "sha256:a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8"              ],              "index": "pypi", -            "version": "==3.6.0" +            "version": "==3.7.7"          },          "flake8-bugbear": {              "hashes": [ @@ -682,11 +651,11 @@          },          "flake8-import-order": {              "hashes": [ -                "sha256:9be5ca10d791d458eaa833dd6890ab2db37be80384707b0f76286ddd13c16cbf", -                "sha256:feca2fd0a17611b33b7fa84449939196c2c82764e262486d5c3e143ed77d387b" +                "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543", +                "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"              ],              "index": "pypi", -            "version": "==0.18" +            "version": "==0.18.1"          },          "flake8-string-format": {              "hashes": [ @@ -698,11 +667,11 @@          },          "flake8-tidy-imports": {              "hashes": [ -                "sha256:5fc28c82bba16abb4f1154dc59a90487f5491fbdb27e658cbee241e8fddc1b91", -                "sha256:c05c9f7dadb5748a04b6fa1c47cb6ae5a8170f03cfb1dca8b37aec58c1ee6d15" +                "sha256:1c476aabc6e8db26dc75278464a3a392dba0ea80562777c5f13fd5cdf2646154", +                "sha256:b3f5b96affd0f57cacb6621ed28286ce67edaca807757b51227043ebf7b136a1"              ],              "index": "pypi", -            "version": "==1.1.0" +            "version": "==2.0.0"          },          "flake8-todo": {              "hashes": [ @@ -713,10 +682,10 @@          },          "identify": {              "hashes": [ -                "sha256:08826e68e39e7de53cc2ddd8f6228a4e463b4bacb20565e5301c3ec690e68d27", -                "sha256:2364e24a7699fea0dc910e90740adbab43eef3746eeea4e016029c34123ce66d" +                "sha256:407cbb36e8b72b45cfa96a97ae13ccabca4c36557e03616958bd895dfcd3f77d", +                "sha256:721abbbb1269fa1172799119981c22c5ace022544ce82eedc29b1b0d753baaa5"              ], -            "version": "==1.1.8" +            "version": "==1.4.0"          },          "idna": {              "hashes": [ @@ -732,6 +701,14 @@              ],              "version": "==0.8"          }, +        "importlib-resources": { +            "hashes": [ +                "sha256:6e2783b2538bd5a14678284a3962b0660c715e5a0f10243fd5e00a4b5974f50b", +                "sha256:d3279fd0f6f847cced9f7acc19bd3e5df54d34f93a2e7bb5f238f81545787078" +            ], +            "markers": "python_version < '3.7'", +            "version": "==1.0.2" +        },          "mccabe": {              "hashes": [                  "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", @@ -747,39 +724,39 @@          },          "packaging": {              "hashes": [ -                "sha256:0886227f54515e592aaa2e5a553332c73962917f2831f1b0f9b9f4380a4b9807", -                "sha256:f95a1e147590f204328170981833854229bb2912ac3d5f89e2a8ccd2834800c9" +                "sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", +                "sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"              ], -            "version": "==18.0" +            "version": "==19.0"          },          "pre-commit": {              "hashes": [ -                "sha256:33bb9bf599c334d458fa9e311bde54e0c306a651473b6a36fdb36a61c8605c89", -                "sha256:e233f5cf3230ae9ed9ada132e9cf6890e18cc937adc669353fb64394f6e80c17" +                "sha256:d3d69c63ae7b7584c4b51446b0b583d454548f9df92575b2fe93a68ec800c4d3", +                "sha256:fc512f129b9526e35e80d656a16a31c198f584c4fce3a5c739045b5140584917"              ],              "index": "pypi", -            "version": "==1.13.0" +            "version": "==1.14.4"          },          "pycodestyle": {              "hashes": [ -                "sha256:cbc619d09254895b0d12c2c691e237b2e91e9b2ecf5e84c26b35400f93dcfb83", -                "sha256:cbfca99bd594a10f674d0cd97a3d802a1fdef635d4361e1a2658de47ed261e3a" +                "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", +                "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"              ], -            "version": "==2.4.0" +            "version": "==2.5.0"          },          "pyflakes": {              "hashes": [ -                "sha256:9a7662ec724d0120012f6e29d6248ae3727d821bba522a0e6b356eff19126a49", -                "sha256:f661252913bc1dbe7fcfcbf0af0db3f42ab65aabd1a6ca68fe5d466bace94dae" +                "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", +                "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"              ], -            "version": "==2.0.0" +            "version": "==2.1.1"          },          "pyparsing": {              "hashes": [ -                "sha256:40856e74d4987de5d01761a22d1621ae1c7f8774585acae358aa5c5936c6c90b", -                "sha256:f353aab21fd474459d97b709e527b5571314ee5f067441dc9f88e33eecd96592" +                "sha256:66c9268862641abcac4a96ba74506e594c884e3f57690a696d21ad8210ed667a", +                "sha256:f6c5ef0d7480ad048c054c37632c67fca55299990fff127850181659eea33fc3"              ], -            "version": "==2.3.0" +            "version": "==2.3.1"          },          "pyyaml": {              "hashes": [ @@ -808,11 +785,11 @@          },          "safety": {              "hashes": [ -                "sha256:399511524f47230d5867f1eb75548f9feefb7a2711a4985cb5be0e034f87040f", -                "sha256:69b970918324865dcd7b92337e07152a0ea1ceecaf92f4d3b38529ee0ca83441" +                "sha256:0a3a8a178a9c96242b224f033ee8d1d130c0448b0e6622d12deaf37f6c3b4e59", +                "sha256:5059f3ffab3648330548ea9c7403405bbfaf085b11235770825d14c58f24cb78"              ],              "index": "pypi", -            "version": "==1.8.4" +            "version": "==1.8.5"          },          "six": {              "hashes": [ @@ -837,9 +814,10 @@          },          "virtualenv": {              "hashes": [ -                "sha256:34b9ae3742abed2f95d3970acf4d80533261d6061b51160b197f84e5b4c98b4c" +                "sha256:6aebaf4dd2568a0094225ebbca987859e369e3e5c22dc7d52e5406d504890417", +                "sha256:984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39"              ], -            "version": "==16.2.0" +            "version": "==16.4.3"          },          "zipp": {              "hashes": [ diff --git a/bot/__init__.py b/bot/__init__.py index 54550842e..a088138a0 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -91,5 +91,4 @@ for key, value in logging.Logger.manager.loggerDict.items():  # Silence irrelevant loggers  logging.getLogger("aio_pika").setLevel(logging.ERROR)  logging.getLogger("discord").setLevel(logging.ERROR) -logging.getLogger("PIL").setLevel(logging.ERROR)  logging.getLogger("websockets").setLevel(logging.ERROR) diff --git a/bot/__main__.py b/bot/__main__.py index 6928a3960..ead6d287a 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -72,7 +72,6 @@ bot.load_extension("bot.cogs.off_topic_names")  bot.load_extension("bot.cogs.reddit")  bot.load_extension("bot.cogs.reminders")  bot.load_extension("bot.cogs.site") -bot.load_extension("bot.cogs.snakes")  bot.load_extension("bot.cogs.snekbox")  bot.load_extension("bot.cogs.superstarify")  bot.load_extension("bot.cogs.tags") diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py deleted file mode 100644 index d74380259..000000000 --- a/bot/cogs/snakes.py +++ /dev/null @@ -1,1216 +0,0 @@ -import asyncio -import colorsys -import logging -import os -import random -import re -import string -import textwrap -import urllib -from functools import partial -from io import BytesIO -from typing import Any, Dict - -import aiohttp -import async_timeout -from discord import Colour, Embed, File, Member, Message, Reaction -from discord.ext.commands import BadArgument, Bot, Context, bot_has_permissions, group -from PIL import Image, ImageDraw, ImageFont - -from bot.constants import ERROR_REPLIES, Keys, URLs -from bot.converters import Snake -from bot.decorators import locked -from bot.utils.snakes import hatching, perlin, perlinsneks, sal - - -log = logging.getLogger(__name__) - - -# region: Constants -# Color -SNAKE_COLOR = 0x399600 - -# Antidote constants -SYRINGE_EMOJI = "\U0001F489"  # :syringe: -PILL_EMOJI = "\U0001F48A"     # :pill: -HOURGLASS_EMOJI = "\u231B"    # :hourglass: -CROSSBONES_EMOJI = "\u2620"   # :skull_crossbones: -ALEMBIC_EMOJI = "\u2697"      # :alembic: -TICK_EMOJI = "\u2705"         # :white_check_mark: - Correct peg, correct hole -CROSS_EMOJI = "\u274C"        # :x: - Wrong peg, wrong hole -BLANK_EMOJI = "\u26AA"        # :white_circle: - Correct peg, wrong hole -HOLE_EMOJI = "\u2B1C"         # :white_square: - Used in guesses -EMPTY_UNICODE = "\u200b"      # literally just an empty space - -ANTIDOTE_EMOJI = ( -    SYRINGE_EMOJI, -    PILL_EMOJI, -    HOURGLASS_EMOJI, -    CROSSBONES_EMOJI, -    ALEMBIC_EMOJI, -) - -# Quiz constants -ANSWERS_EMOJI = { -    "a": "\U0001F1E6",  # :regional_indicator_a: 🇦 -    "b": "\U0001F1E7",  # :regional_indicator_b: 🇧 -    "c": "\U0001F1E8",  # :regional_indicator_c: 🇨 -    "d": "\U0001F1E9",  # :regional_indicator_d: 🇩 -} - -ANSWERS_EMOJI_REVERSE = { -    "\U0001F1E6": "A",  # :regional_indicator_a: 🇦 -    "\U0001F1E7": "B",  # :regional_indicator_b: 🇧 -    "\U0001F1E8": "C",  # :regional_indicator_c: 🇨 -    "\U0001F1E9": "D",  # :regional_indicator_d: 🇩 -} - -# Zzzen of pythhhon constant -ZEN = """ -Beautiful is better than ugly. -Explicit is better than implicit. -Simple is better than complex. -Complex is better than complicated. -Flat is better than nested. -Sparse is better than dense. -Readability counts. -Special cases aren't special enough to break the rules. -Although practicality beats purity. -Errors should never pass silently. -Unless explicitly silenced. -In the face of ambiguity, refuse the temptation to guess. -There should be one-- and preferably only one --obvious way to do it. -Now is better than never. -Although never is often better than *right* now. -If the implementation is hard to explain, it's a bad idea. -If the implementation is easy to explain, it may be a good idea. -""" - -# Max messages to train snake_chat on -MSG_MAX = 100 - -# get_snek constants -URL = "https://en.wikipedia.org/w/api.php?" - -# snake guess responses -INCORRECT_GUESS = ( -    "Nope, that's not what it is.", -    "Not quite.", -    "Not even close.", -    "Terrible guess.", -    "Nnnno.", -    "Dude. No.", -    "I thought everyone knew this one.", -    "Guess you suck at snakes.", -    "Bet you feel stupid now.", -    "Hahahaha, no.", -    "Did you hit the wrong key?" -) - -CORRECT_GUESS = ( -    "**WRONG**. Wait, no, actually you're right.", -    "Yeah, you got it!", -    "Yep, that's exactly what it is.", -    "Uh-huh. Yep yep yep.", -    "Yeah that's right.", -    "Yup. How did you know that?", -    "Are you a herpetologist?", -    "Sure, okay, but I bet you can't pronounce it.", -    "Are you cheating?" -) - -# snake card consts -CARD = { -    "top": Image.open("bot/resources/snake_cards/card_top.png"), -    "frame": Image.open("bot/resources/snake_cards/card_frame.png"), -    "bottom": Image.open("bot/resources/snake_cards/card_bottom.png"), -    "backs": [ -        Image.open(f"bot/resources/snake_cards/backs/{file}") -        for file in os.listdir("bot/resources/snake_cards/backs") -    ], -    "font": ImageFont.truetype("bot/resources/snake_cards/expressway.ttf", 20) -} -# endregion - - -class Snakes: -    """ -    Commands related to snakes. These were created by our -    community during the first code jam. - -    More information can be found in the code-jam-1 repo. - -    https://gitlab_bot_repo.com/discord-python/code-jams/code-jam-1 -    """ - -    wiki_brief = re.compile(r'(.*?)(=+ (.*?) =+)', flags=re.DOTALL) -    valid_image_extensions = ('gif', 'png', 'jpeg', 'jpg', 'webp') - -    def __init__(self, bot: Bot): -        self.active_sal = {} -        self.bot = bot -        self.headers = {"X-API-KEY": Keys.site_api} - -    # region: Helper methods -    @staticmethod -    def _beautiful_pastel(hue): -        """ -        Returns random bright pastels. -        """ - -        light = random.uniform(0.7, 0.85) -        saturation = 1 - -        rgb = colorsys.hls_to_rgb(hue, light, saturation) -        hex_rgb = "" - -        for part in rgb: -            value = int(part * 0xFF) -            hex_rgb += f"{value:02x}" - -        return int(hex_rgb, 16) - -    @staticmethod -    def _generate_card(buffer: BytesIO, content: dict) -> BytesIO: -        """ -        Generate a card from snake information. - -        Written by juan and Someone during the first code jam. -        """ - -        snake = Image.open(buffer) - -        # Get the size of the snake icon, configure the height of the image box (yes, it changes) -        icon_width = 347  # Hardcoded, not much i can do about that -        icon_height = int((icon_width / snake.width) * snake.height) -        frame_copies = icon_height // CARD['frame'].height + 1 -        snake.thumbnail((icon_width, icon_height)) - -        # Get the dimensions of the final image -        main_height = icon_height + CARD['top'].height + CARD['bottom'].height -        main_width = CARD['frame'].width - -        # Start creating the foreground -        foreground = Image.new("RGBA", (main_width, main_height), (0, 0, 0, 0)) -        foreground.paste(CARD['top'], (0, 0)) - -        # Generate the frame borders to the correct height -        for offset in range(frame_copies): -            position = (0, CARD['top'].height + offset * CARD['frame'].height) -            foreground.paste(CARD['frame'], position) - -        # Add the image and bottom part of the image -        foreground.paste(snake, (36, CARD['top'].height))  # Also hardcoded :( -        foreground.paste(CARD['bottom'], (0, CARD['top'].height + icon_height)) - -        # Setup the background -        back = random.choice(CARD['backs']) -        back_copies = main_height // back.height + 1 -        full_image = Image.new("RGBA", (main_width, main_height), (0, 0, 0, 0)) - -        # Generate the tiled background -        for offset in range(back_copies): -            full_image.paste(back, (16, 16 + offset * back.height)) - -        # Place the foreground onto the final image -        full_image.paste(foreground, (0, 0), foreground) - -        # Get the first two sentences of the info -        description = '.'.join(content['info'].split(".")[:2]) + '.' - -        # Setup positioning variables -        margin = 36 -        offset = CARD['top'].height + icon_height + margin - -        # Create blank rectangle image which will be behind the text -        rectangle = Image.new( -            "RGBA", -            (main_width, main_height), -            (0, 0, 0, 0) -        ) - -        # Draw a semi-transparent rectangle on it -        rect = ImageDraw.Draw(rectangle) -        rect.rectangle( -            (margin, offset, main_width - margin, main_height - margin), -            fill=(63, 63, 63, 128) -        ) - -        # Paste it onto the final image -        full_image.paste(rectangle, (0, 0), mask=rectangle) - -        # Draw the text onto the final image -        draw = ImageDraw.Draw(full_image) -        for line in textwrap.wrap(description, 36): -            draw.text([margin + 4, offset], line, font=CARD['font']) -            offset += CARD['font'].getsize(line)[1] - -        # Get the image contents as a BufferIO object -        buffer = BytesIO() -        full_image.save(buffer, 'PNG') -        buffer.seek(0) - -        return buffer - -    @staticmethod -    def _snakify(message): -        """ -        Sssnakifffiesss a sstring. -        """ - -        # Replace fricatives with exaggerated snake fricatives. -        simple_fricatives = [ -            "f", "s", "z", "h", -            "F", "S", "Z", "H", -        ] -        complex_fricatives = [ -            "th", "sh", "Th", "Sh" -        ] - -        for letter in simple_fricatives: -            if letter.islower(): -                message = message.replace(letter, letter * random.randint(2, 4)) -            else: -                message = message.replace(letter, (letter * random.randint(2, 4)).title()) - -        for fricative in complex_fricatives: -            message = message.replace(fricative, fricative[0] + fricative[1] * random.randint(2, 4)) - -        return message - -    async def _fetch(self, session, url, params=None): -        """ -        Asyncronous web request helper method. -        """ - -        if params is None: -            params = {} - -        async with async_timeout.timeout(10): -            async with session.get(url, params=params) as response: -                return await response.json() - -    def _get_random_long_message(self, messages, retries=10): -        """ -        Fetch a message that's at least 3 words long, -        but only if it is possible to do so in retries -        attempts. Else, just return whatever the last -        message is. -        """ - -        long_message = random.choice(messages) -        if len(long_message.split()) < 3 and retries > 0: -            return self._get_random_long_message( -                messages, -                retries=retries - 1 -            ) - -        return long_message - -    async def _get_snek(self, name: str) -> Dict[str, Any]: -        """ -        Goes online and fetches all the data from a wikipedia article -        about a snake. Builds a dict that the .get() method can use. - -        Created by Ava and eivl. - -        :param name: The name of the snake to get information for - omit for a random snake -        :return: A dict containing information on a snake -        """ - -        snake_info = {} - -        async with aiohttp.ClientSession() as session: -            params = { -                'format': 'json', -                'action': 'query', -                'list': 'search', -                'srsearch': name, -                'utf8': '', -                'srlimit': '1', -            } - -            json = await self._fetch(session, URL, params=params) - -            # wikipedia does have a error page -            try: -                pageid = json["query"]["search"][0]["pageid"] -            except KeyError: -                # Wikipedia error page ID(?) -                pageid = 41118 -            except IndexError: -                return None - -            params = { -                'format': 'json', -                'action': 'query', -                'prop': 'extracts|images|info', -                'exlimit': 'max', -                'explaintext': '', -                'inprop': 'url', -                'pageids': pageid -            } - -            json = await self._fetch(session, URL, params=params) - -            # constructing dict - handle exceptions later -            try: -                snake_info["title"] = json["query"]["pages"][f"{pageid}"]["title"] -                snake_info["extract"] = json["query"]["pages"][f"{pageid}"]["extract"] -                snake_info["images"] = json["query"]["pages"][f"{pageid}"]["images"] -                snake_info["fullurl"] = json["query"]["pages"][f"{pageid}"]["fullurl"] -                snake_info["pageid"] = json["query"]["pages"][f"{pageid}"]["pageid"] -            except KeyError: -                snake_info["error"] = True - -            if snake_info["images"]: -                i_url = 'https://commons.wikimedia.org/wiki/Special:FilePath/' -                image_list = [] -                map_list = [] -                thumb_list = [] - -                # Wikipedia has arbitrary images that are not snakes -                banned = [ -                    'Commons-logo.svg', -                    'Red%20Pencil%20Icon.png', -                    'distribution', -                    'The%20Death%20of%20Cleopatra%20arthur.jpg', -                    'Head%20of%20holotype', -                    'locator', -                    'Woma.png', -                    '-map.', -                    '.svg', -                    'ange.', -                    'Adder%20(PSF).png' -                ] - -                for image in snake_info["images"]: -                    # images come in the format of `File:filename.extension` -                    file, sep, filename = image["title"].partition(':') -                    filename = filename.replace(" ", "%20")  # Wikipedia returns good data! - -                    if not filename.startswith('Map'): -                        if any(ban in filename for ban in banned): -                            pass -                        else: -                            image_list.append(f"{i_url}{filename}") -                            thumb_list.append(f"{i_url}{filename}?width=100") -                    else: -                        map_list.append(f"{i_url}{filename}") - -            snake_info["image_list"] = image_list -            snake_info["map_list"] = map_list -            snake_info["thumb_list"] = thumb_list -            snake_info["name"] = name - -            match = self.wiki_brief.match(snake_info['extract']) -            info = match.group(1) if match else None - -            if info: -                info = info.replace("\n", "\n\n")  # Give us some proper paragraphs. - -            snake_info["info"] = info - -        return snake_info - -    async def _get_snake_name(self) -> Dict[str, str]: -        """ -        Gets a random snake name. -        :return: A random snake name, as a string. -        """ - -        response = await self.bot.http_session.get(URLs.site_names_api, headers=self.headers) -        name_data = await response.json() - -        return name_data - -    async def _validate_answer(self, ctx: Context, message: Message, answer: str, options: list): -        """ -        Validate the answer using a reaction event loop -        :return: -        """ - -        def predicate(reaction, user): -            """ -            Test if the the answer is valid and can be evaluated. -            """ -            return ( -                reaction.message.id == message.id                  # The reaction is attached to the question we asked. -                and user == ctx.author                             # It's the user who triggered the quiz. -                and str(reaction.emoji) in ANSWERS_EMOJI.values()  # The reaction is one of the options. -            ) - -        for emoji in ANSWERS_EMOJI.values(): -            await message.add_reaction(emoji) - -        # Validate the answer -        try: -            reaction, user = await ctx.bot.wait_for("reaction_add", timeout=45.0, check=predicate) -        except asyncio.TimeoutError: -            await ctx.channel.send(f"You took too long. The correct answer was **{options[answer]}**.") -            await message.clear_reactions() -            return - -        if str(reaction.emoji) == ANSWERS_EMOJI[answer]: -            await ctx.send(f"{random.choice(CORRECT_GUESS)} The correct answer was **{options[answer]}**.") -        else: -            await ctx.send( -                f"{random.choice(INCORRECT_GUESS)} The correct answer was **{options[answer]}**." -            ) - -        await message.clear_reactions() -    # endregion - -    # region: Commands -    @group(name='snakes', aliases=('snake',), invoke_without_command=True) -    async def snakes_group(self, ctx: Context): -        """Commands from our first code jam.""" - -        await ctx.invoke(self.bot.get_command("help"), "snake") - -    @bot_has_permissions(manage_messages=True) -    @snakes_group.command(name='antidote') -    @locked() -    async def antidote_command(self, ctx: Context): -        """ -        Antidote - Can you create the antivenom before the patient dies? - -        Rules:  You have 4 ingredients for each antidote, you only have 10 attempts -                Once you synthesize the antidote, you will be presented with 4 markers -                Tick: This means you have a CORRECT ingredient in the CORRECT position -                Circle: This means you have a CORRECT ingredient in the WRONG position -                Cross: This means you have a WRONG ingredient in the WRONG position - -        Info:   The game automatically ends after 5 minutes inactivity. -                You should only use each ingredient once. - -        This game was created by Lord Bisk and Runew0lf. -        """ - -        def predicate(reaction_: Reaction, user_: Member): -            """ -            Make sure that this reaction is what we want to operate on -            """ - -            return ( -                all(( -                    reaction_.message.id == board_id.id,  # Reaction is on this message -                    reaction_.emoji in ANTIDOTE_EMOJI,    # Reaction is one of the pagination emotes -                    user_.id != self.bot.user.id,         # Reaction was not made by the Bot -                    user_.id == ctx.author.id             # Reaction was made by author -                )) -            ) - -        # Initialize variables -        antidote_tries = 0 -        antidote_guess_count = 0 -        antidote_guess_list = [] -        guess_result = [] -        board = [] -        page_guess_list = [] -        page_result_list = [] -        win = False - -        antidote_embed = Embed(color=SNAKE_COLOR, title="Antidote") -        antidote_embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) - -        # Generate answer -        antidote_answer = list(ANTIDOTE_EMOJI)  # Duplicate list, not reference it -        random.shuffle(antidote_answer) -        antidote_answer.pop() - -        # Begin initial board building -        for i in range(0, 10): -            page_guess_list.append(f"{HOLE_EMOJI} {HOLE_EMOJI} {HOLE_EMOJI} {HOLE_EMOJI}") -            page_result_list.append(f"{CROSS_EMOJI} {CROSS_EMOJI} {CROSS_EMOJI} {CROSS_EMOJI}") -            board.append(f"`{i+1:02d}` " -                         f"{page_guess_list[i]} - " -                         f"{page_result_list[i]}") -            board.append(EMPTY_UNICODE) -        antidote_embed.add_field(name="10 guesses remaining", value="\n".join(board)) -        board_id = await ctx.send(embed=antidote_embed)  # Display board - -        # Add our player reactions -        for emoji in ANTIDOTE_EMOJI: -            await board_id.add_reaction(emoji) - -        # Begin main game loop -        while not win and antidote_tries < 10: -            try: -                reaction, user = await ctx.bot.wait_for("reaction_add", timeout=300, check=predicate) -            except asyncio.TimeoutError: -                log.debug("Antidote timed out waiting for a reaction") -                break  # We're done, no reactions for the last 5 minutes - -            if antidote_tries < 10: -                if antidote_guess_count < 4: -                    if reaction.emoji in ANTIDOTE_EMOJI: -                        antidote_guess_list.append(reaction.emoji) -                        antidote_guess_count += 1 - -                    if antidote_guess_count == 4:  # Guesses complete -                        antidote_guess_count = 0 -                        page_guess_list[antidote_tries] = " ".join(antidote_guess_list) - -                        # Now check guess -                        for i in range(0, len(antidote_answer)): -                            if antidote_guess_list[i] == antidote_answer[i]: -                                guess_result.append(TICK_EMOJI) -                            elif antidote_guess_list[i] in antidote_answer: -                                guess_result.append(BLANK_EMOJI) -                            else: -                                guess_result.append(CROSS_EMOJI) -                        guess_result.sort() -                        page_result_list[antidote_tries] = " ".join(guess_result) - -                        # Rebuild the board -                        board = [] -                        for i in range(0, 10): -                            board.append(f"`{i+1:02d}` " -                                         f"{page_guess_list[i]} - " -                                         f"{page_result_list[i]}") -                            board.append(EMPTY_UNICODE) - -                        # Remove Reactions -                        for emoji in antidote_guess_list: -                            await board_id.remove_reaction(emoji, user) - -                        if antidote_guess_list == antidote_answer: -                            win = True - -                        antidote_tries += 1 -                        guess_result = [] -                        antidote_guess_list = [] - -                        antidote_embed.clear_fields() -                        antidote_embed.add_field(name=f"{10 - antidote_tries} " -                                                      f"guesses remaining", -                                                 value="\n".join(board)) -                        # Redisplay the board -                        await board_id.edit(embed=antidote_embed) - -        # Winning / Ending Screen -        if win is True: -            antidote_embed = Embed(color=SNAKE_COLOR, title="Antidote") -            antidote_embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) -            antidote_embed.set_image(url="https://i.makeagif.com/media/7-12-2015/Cj1pts.gif") -            antidote_embed.add_field(name=f"You have created the snake antidote!", -                                     value=f"The solution was: {' '.join(antidote_answer)}\n" -                                           f"You had {10 - antidote_tries} tries remaining.") -            await board_id.edit(embed=antidote_embed) -        else: -            antidote_embed = Embed(color=SNAKE_COLOR, title="Antidote") -            antidote_embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) -            antidote_embed.set_image(url="https://media.giphy.com/media/ceeN6U57leAhi/giphy.gif") -            antidote_embed.add_field(name=EMPTY_UNICODE, -                                     value=f"Sorry you didnt make the antidote in time.\n" -                                           f"The formula was {' '.join(antidote_answer)}") -            await board_id.edit(embed=antidote_embed) - -        log.debug("Ending pagination and removing all reactions...") -        await board_id.clear_reactions() - -    @snakes_group.command(name='draw') -    async def draw_command(self, ctx: Context): -        """ -        Draws a random snek using Perlin noise - -        Written by Momo and kel. -        Modified by juan and lemon. -        """ - -        with ctx.typing(): - -            # Generate random snake attributes -            width = random.randint(6, 10) -            length = random.randint(15, 22) -            random_hue = random.random() -            snek_color = self._beautiful_pastel(random_hue) -            text_color = self._beautiful_pastel((random_hue + 0.5) % 1) -            bg_color = ( -                random.randint(32, 50), -                random.randint(32, 50), -                random.randint(50, 70), -            ) - -            # Get a snake idiom from the API -            response = await self.bot.http_session.get(URLs.site_idioms_api, headers=self.headers) -            text = await response.json() - -            # Build and send the snek -            factory = perlin.PerlinNoiseFactory(dimension=1, octaves=2) -            image_frame = perlinsneks.create_snek_frame( -                factory, -                snake_width=width, -                snake_length=length, -                snake_color=snek_color, -                text=text, -                text_color=text_color, -                bg_color=bg_color -            ) -            png_bytes = perlinsneks.frame_to_png_bytes(image_frame) - -            file = File(png_bytes, filename='snek.png') - -            await ctx.send(file=file) - -    @snakes_group.command(name='get') -    @bot_has_permissions(manage_messages=True) -    @locked() -    async def get_command(self, ctx: Context, *, name: Snake = None): -        """ -        Fetches information about a snake from Wikipedia. -        :param ctx: Context object passed from discord.py -        :param name: Optional, the name of the snake to get information for - omit for a random snake - -        Created by Ava and eivl. -        """ - -        with ctx.typing(): -            if name is None: -                name = await Snake.random() - -            if isinstance(name, dict): -                data = name -            else: -                data = await self._get_snek(name) - -            if data.get('error'): -                return await ctx.send('Could not fetch data from Wikipedia.') - -            description = data["info"] - -            # Shorten the description if needed -            if len(description) > 1000: -                description = description[:1000] -                last_newline = description.rfind("\n") -                if last_newline > 0: -                    description = description[:last_newline] - -            # Strip and add the Wiki link. -            if "fullurl" in data: -                description = description.strip("\n") -                description += f"\n\nRead more on [Wikipedia]({data['fullurl']})" - -            # Build and send the embed. -            embed = Embed( -                title=data.get("title", data.get('name')), -                description=description, -                colour=0x59982F, -            ) - -            emoji = 'https://emojipedia-us.s3.amazonaws.com/thumbs/60/google/3/snake_1f40d.png' -            image = next((url for url in data['image_list'] if url.endswith(self.valid_image_extensions)), emoji) -            embed.set_image(url=image) - -            await ctx.send(embed=embed) - -    @snakes_group.command(name='guess', aliases=('identify',)) -    @locked() -    async def guess_command(self, ctx): -        """ -        Snake identifying game! - -        Made by Ava and eivl. -        Modified by lemon. -        """ - -        with ctx.typing(): - -            image = None - -            while image is None: -                snakes = [await Snake.random() for _ in range(4)] -                snake = random.choice(snakes) -                answer = "abcd"[snakes.index(snake)] - -                data = await self._get_snek(snake) - -                image = next((url for url in data['image_list'] if url.endswith(self.valid_image_extensions)), None) - -            embed = Embed( -                title='Which of the following is the snake in the image?', -                description="\n".join(f"{'ABCD'[snakes.index(snake)]}: {snake}" for snake in snakes), -                colour=SNAKE_COLOR -            ) -            embed.set_image(url=image) - -        guess = await ctx.send(embed=embed) -        options = {f"{'abcd'[snakes.index(snake)]}": snake for snake in snakes} -        await self._validate_answer(ctx, guess, answer, options) - -    @snakes_group.command(name='hatch') -    async def hatch_command(self, ctx: Context): -        """ -        Hatches your personal snake - -        Written by Momo and kel. -        """ - -        # Pick a random snake to hatch. -        snake_name = random.choice(list(hatching.snakes.keys())) -        snake_image = hatching.snakes[snake_name] - -        # Hatch the snake -        message = await ctx.channel.send(embed=Embed(description="Hatching your snake :snake:...")) -        await asyncio.sleep(1) - -        for stage in hatching.stages: -            hatch_embed = Embed(description=stage) -            await message.edit(embed=hatch_embed) -            await asyncio.sleep(1) -        await asyncio.sleep(1) -        await message.delete() - -        # Build and send the embed. -        my_snake_embed = Embed(description=":tada: Congrats! You hatched: **{0}**".format(snake_name)) -        my_snake_embed.set_thumbnail(url=snake_image) -        my_snake_embed.set_footer( -            text=" Owner: {0}#{1}".format(ctx.message.author.name, ctx.message.author.discriminator) -        ) - -        await ctx.channel.send(embed=my_snake_embed) - -    @snakes_group.command(name='movie') -    async def movie_command(self, ctx: Context): -        """ -        Gets a random snake-related movie from OMDB. - -        Written by Samuel. -        Modified by gdude. -        """ - -        url = "http://www.omdbapi.com/" -        page = random.randint(1, 27) - -        response = await self.bot.http_session.get( -            url, -            params={ -                "s": "snake", -                "page": page, -                "type": "movie", -                "apikey": Keys.omdb -            } -        ) -        data = await response.json() -        movie = random.choice(data["Search"])["imdbID"] - -        response = await self.bot.http_session.get( -            url, -            params={ -                "i": movie, -                "apikey": Keys.omdb -            } -        ) -        data = await response.json() - -        embed = Embed( -            title=data["Title"], -            color=SNAKE_COLOR -        ) - -        del data["Response"], data["imdbID"], data["Title"] - -        for key, value in data.items(): -            if not value or value == "N/A" or key in ("Response", "imdbID", "Title", "Type"): -                continue - -            if key == "Ratings":  # [{'Source': 'Internet Movie Database', 'Value': '7.6/10'}] -                rating = random.choice(value) - -                if rating["Source"] != "Internet Movie Database": -                    embed.add_field(name=f"Rating: {rating['Source']}", value=rating["Value"]) - -                continue - -            if key == "Poster": -                embed.set_image(url=value) -                continue - -            elif key == "imdbRating": -                key = "IMDB Rating" - -            elif key == "imdbVotes": -                key = "IMDB Votes" - -            embed.add_field(name=key, value=value, inline=True) - -        embed.set_footer(text="Data provided by the OMDB API") - -        await ctx.channel.send( -            embed=embed -        ) - -    @snakes_group.command(name='quiz') -    @locked() -    async def quiz_command(self, ctx: Context): -        """ -        Asks a snake-related question in the chat and validates the user's guess. - -        This was created by Mushy and Cardium, -        and modified by Urthas and lemon. -        """ - -        # Prepare a question. -        response = await self.bot.http_session.get(URLs.site_quiz_api, headers=self.headers) -        question = await response.json() -        answer = question["answerkey"] -        options = {key: question["options"][key] for key in ANSWERS_EMOJI.keys()} - -        # Build and send the embed. -        embed = Embed( -            color=SNAKE_COLOR, -            title=question["question"], -            description="\n".join( -                [f"**{key.upper()}**: {answer}" for key, answer in options.items()] -            ) -        ) - -        quiz = await ctx.channel.send("", embed=embed) -        await self._validate_answer(ctx, quiz, answer, options) - -    @snakes_group.command(name='name', aliases=('name_gen',)) -    async def name_command(self, ctx: Context, *, name: str = None): -        """ -        Slices the users name at the last vowel (or second last if the name -        ends with a vowel), and then combines it with a random snake name, -        which is sliced at the first vowel (or second if the name starts with -        a vowel). - -        If the name contains no vowels, it just appends the snakename -        to the end of the name. - -        Examples: -            lemon + anaconda = lemoconda -            krzsn + anaconda = krzsnconda -            gdude + anaconda = gduconda -            aperture + anaconda = apertuconda -            lucy + python = luthon -            joseph + taipan = joseipan - -        This was written by Iceman, and modified for inclusion into the bot by lemon. -        """ - -        snake_name = await self._get_snake_name() -        snake_name = snake_name['name'] -        snake_prefix = "" - -        # Set aside every word in the snake name except the last. -        if " " in snake_name: -            snake_prefix = " ".join(snake_name.split()[:-1]) -            snake_name = snake_name.split()[-1] - -        # If no name is provided, use whoever called the command. -        if name: -            user_name = name -        else: -            user_name = ctx.author.display_name - -        # Get the index of the vowel to slice the username at -        user_slice_index = len(user_name) -        for index, char in enumerate(reversed(user_name)): -            if index == 0: -                continue -            if char.lower() in "aeiouy": -                user_slice_index -= index -                break - -        # Now, get the index of the vowel to slice the snake_name at -        snake_slice_index = 0 -        for index, char in enumerate(snake_name): -            if index == 0: -                continue -            if char.lower() in "aeiouy": -                snake_slice_index = index + 1 -                break - -        # Combine! -        snake_name = snake_name[snake_slice_index:] -        user_name = user_name[:user_slice_index] -        result = f"{snake_prefix} {user_name}{snake_name}" -        result = string.capwords(result) - -        # Embed and send -        embed = Embed( -            title="Snake name", -            description=f"Your snake-name is **{result}**", -            color=SNAKE_COLOR -        ) - -        return await ctx.send(embed=embed) - -    @snakes_group.command(name='sal') -    @locked() -    async def sal_command(self, ctx: Context): -        """ -        Play a game of Snakes and Ladders! - -        Written by Momo and kel. -        Modified by lemon. -        """ - -        # check if there is already a game in this channel -        if ctx.channel in self.active_sal: -            await ctx.send(f"{ctx.author.mention} A game is already in progress in this channel.") -            return - -        game = sal.SnakeAndLaddersGame(snakes=self, context=ctx) -        self.active_sal[ctx.channel] = game - -        await game.open_game() - -    @snakes_group.command(name='about') -    async def about_command(self, ctx: Context): -        """ -        A command that shows an embed with information about the event, -        it's participants, and its winners. -        """ - -        contributors = [ -            "<@!245270749919576066>", -            "<@!396290259907903491>", -            "<@!172395097705414656>", -            "<@!361708843425726474>", -            "<@!300302216663793665>", -            "<@!210248051430916096>", -            "<@!174588005745557505>", -            "<@!87793066227822592>", -            "<@!211619754039967744>", -            "<@!97347867923976192>", -            "<@!136081839474343936>", -            "<@!263560579770220554>", -            "<@!104749643715387392>", -            "<@!303940835005825024>", -        ] - -        embed = Embed( -            title="About the snake cog", -            description=( -                "The features in this cog were created by members of the community " -                "during our first ever [code jam event](https://gitlab.com/discord-python/code-jams/code-jam-1). \n\n" -                "The event saw over 50 participants, who competed to write a discord bot cog with a snake theme over " -                "48 hours. The staff then selected the best features from all the best teams, and made modifications " -                "to ensure they would all work together before integrating them into the community bot.\n\n" -                "It was a tight race, but in the end, <@!104749643715387392> and <@!303940835005825024> " -                "walked away as grand champions. Make sure you check out `!snakes sal`, `!snakes draw` " -                "and `!snakes hatch` to see what they came up with." -            ) -        ) - -        embed.add_field( -            name="Contributors", -            value=( -                ", ".join(contributors) -            ) -        ) - -        await ctx.channel.send(embed=embed) - -    @snakes_group.command(name='card') -    async def card_command(self, ctx: Context, *, name: Snake = None): -        """ -        Create an interesting little card from a snake! - -        Created by juan and Someone during the first code jam. -        """ - -        # Get the snake data we need -        if not name: -            name_obj = await self._get_snake_name() -            name = name_obj['scientific'] -            content = await self._get_snek(name) - -        elif isinstance(name, dict): -            content = name - -        else: -            content = await self._get_snek(name) - -        # Make the card -        async with ctx.typing(): - -            stream = BytesIO() -            async with async_timeout.timeout(10): -                async with self.bot.http_session.get(content['image_list'][0]) as response: -                    stream.write(await response.read()) - -            stream.seek(0) - -            func = partial(self._generate_card, stream, content) -            final_buffer = await self.bot.loop.run_in_executor(None, func) - -        # Send it! -        await ctx.send( -            f"A wild {content['name'].title()} appears!", -            file=File(final_buffer, filename=content['name'].replace(" ", "") + ".png") -        ) - -    @snakes_group.command(name='fact') -    async def fact_command(self, ctx: Context): -        """ -        Gets a snake-related fact - -        Written by Andrew and Prithaj. -        Modified by lemon. -        """ - -        # Get a fact from the API. -        response = await self.bot.http_session.get(URLs.site_facts_api, headers=self.headers) -        question = await response.json() - -        # Build and send the embed. -        embed = Embed( -            title="Snake fact", -            color=SNAKE_COLOR, -            description=question -        ) -        await ctx.channel.send(embed=embed) - -    @snakes_group.command(name='help') -    async def help_command(self, ctx: Context): -        """ -        This just invokes the help command on this cog. -        """ - -        log.debug(f"{ctx.author} requested info about the snakes cog") -        return await ctx.invoke(self.bot.get_command("help"), "Snakes") - -    @snakes_group.command(name='snakify') -    async def snakify_command(self, ctx: Context, *, message: str = None): -        """ -        How would I talk if I were a snake? -        :param ctx: context -        :param message: If this is passed, it will snakify the message. -                        If not, it will snakify a random message from -                        the users history. - -        Written by Momo and kel. -        Modified by lemon. -        """ - -        with ctx.typing(): -            embed = Embed() -            user = ctx.message.author - -            if not message: - -                # Get a random message from the users history -                messages = [] -                async for message in ctx.channel.history(limit=500).filter( -                        lambda msg: msg.author == ctx.message.author  # Message was sent by author. -                ): -                    messages.append(message.content) - -                message = self._get_random_long_message(messages) - -            # Set the avatar -            if user.avatar is not None: -                avatar = f"https://cdn.discordapp.com/avatars/{user.id}/{user.avatar}" -            else: -                avatar = ctx.author.default_avatar_url - -            # Build and send the embed -            embed.set_author( -                name=f"{user.name}#{user.discriminator}", -                icon_url=avatar, -            ) -            embed.description = f"*{self._snakify(message)}*" - -            await ctx.channel.send(embed=embed) - -    @snakes_group.command(name='video', aliases=('get_video',)) -    async def video_command(self, ctx: Context, *, search: str = None): -        """ -        Gets a YouTube video about snakes -        :param name: Optional, a name of a snake. Used to search for videos with that name -        :param ctx: Context object passed from discord.py - -        Written by Andrew and Prithaj. -        """ - -        # Are we searching for anything specific? -        if search: -            query = search + ' snake' -        else: -            snake = await self._get_snake_name() -            query = snake['name'] - -        # Build the URL and make the request -        url = f'https://www.googleapis.com/youtube/v3/search' -        response = await self.bot.http_session.get( -            url, -            params={ -                "part": "snippet", -                "q": urllib.parse.quote(query), -                "type": "video", -                "key": Keys.youtube -            } -        ) -        response = await response.json() -        data = response['items'] - -        # Send the user a video -        if len(data) > 0: -            num = random.randint(0, len(data) - 1) -            youtube_base_url = 'https://www.youtube.com/watch?v=' -            await ctx.channel.send( -                content=f"{youtube_base_url}{data[num]['id']['videoId']}" -            ) -        else: -            log.warning(f"YouTube API error. Full response looks like {response}") - -    @snakes_group.command(name='zen') -    async def zen_command(self, ctx: Context): -        """ -        Gets a random quote from the Zen of Python, -        except as if spoken by a snake. - -        Written by Prithaj and Andrew. -        Modified by lemon. -        """ - -        embed = Embed( -            title="Zzzen of Pythhon", -            color=SNAKE_COLOR -        ) - -        # Get the zen quote and snakify it -        zen_quote = random.choice(ZEN.splitlines()) -        zen_quote = self._snakify(zen_quote) - -        # Embed and send -        embed.description = zen_quote -        await ctx.channel.send( -            embed=embed -        ) -    # endregion - -    # region: Error handlers -    @get_command.error -    @card_command.error -    @video_command.error -    async def command_error(self, ctx, error): - -        embed = Embed() -        embed.colour = Colour.red() - -        if isinstance(error, BadArgument): -            embed.description = str(error) -            embed.title = random.choice(ERROR_REPLIES) - -        elif isinstance(error, OSError): -            log.error(f"snake_card encountered an OSError: {error} ({error.original})") -            embed.description = "Could not generate the snake card! Please try again." -            embed.title = random.choice(ERROR_REPLIES) - -        else: -            log.error(f"Unhandled tag command error: {error} ({error.original})") -            return - -        await ctx.send(embed=embed) -    # endregion - - -def setup(bot): -    bot.add_cog(Snakes(bot)) -    log.info("Cog loaded: Snakes") diff --git a/bot/constants.py b/bot/constants.py index 99afc1e6e..93f8a231b 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -387,9 +387,7 @@ class Keys(metaclass=YAMLGetter):      deploy_bot: str      deploy_site: str -    omdb: str      site_api: str -    youtube: str  class RabbitMQ(metaclass=YAMLGetter): @@ -412,25 +410,19 @@ class URLs(metaclass=YAMLGetter):      bot_avatar: str      deploy: str      gitlab_bot_repo: str -    omdb: str      status: str      # Site endpoints      site: str      site_api: str -    site_facts_api: str      site_clean_api: str      site_superstarify_api: str -    site_idioms_api: str      site_logs_api: str      site_logs_view: str -    site_names_api: str -    site_quiz_api: str      site_reminders_api: str      site_reminders_user_api: str      site_schema: str      site_settings_api: str -    site_special_api: str      site_tags_api: str      site_user_api: str      site_user_complete_api: str diff --git a/bot/converters.py b/bot/converters.py index 069e841f9..91f30ac5e 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -1,117 +1,14 @@  import logging -import random -import socket  from ssl import CertificateError  import discord -from aiohttp import AsyncResolver, ClientConnectorError, ClientSession, TCPConnector +from aiohttp import ClientConnectorError  from discord.ext.commands import BadArgument, Context, Converter -from fuzzywuzzy import fuzz - -from bot.constants import DEBUG_MODE, Keys, URLs -from bot.utils import disambiguate  log = logging.getLogger(__name__) -class Snake(Converter): -    snakes = None -    special_cases = None - -    async def convert(self, ctx, name): -        await self.build_list() -        name = name.lower() - -        if name == 'python': -            return 'Python (programming language)' - -        def get_potential(iterable, *, threshold=80): -            nonlocal name -            potential = [] - -            for item in iterable: -                original, item = item, item.lower() - -                if name == item: -                    return [original] - -                a, b = fuzz.ratio(name, item), fuzz.partial_ratio(name, item) -                if a >= threshold or b >= threshold: -                    potential.append(original) - -            return potential - -        # Handle special cases -        if name.lower() in self.special_cases: -            return self.special_cases.get(name.lower(), name.lower()) - -        names = {snake['name']: snake['scientific'] for snake in self.snakes} -        all_names = names.keys() | names.values() -        timeout = len(all_names) * (3 / 4) - -        embed = discord.Embed(title='Found multiple choices. Please choose the correct one.', colour=0x59982F) -        embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url) - -        name = await disambiguate(ctx, get_potential(all_names), timeout=timeout, embed=embed) -        return names.get(name, name) - -    @classmethod -    async def build_list(cls): - -        headers = {"X-API-KEY": Keys.site_api} - -        # Set up the session -        if DEBUG_MODE: -            http_session = ClientSession( -                connector=TCPConnector( -                    resolver=AsyncResolver(), -                    family=socket.AF_INET, -                    verify_ssl=False, -                ) -            ) -        else: -            http_session = ClientSession( -                connector=TCPConnector( -                    resolver=AsyncResolver() -                ) -            ) - -        # Get all the snakes -        if cls.snakes is None: -            response = await http_session.get( -                URLs.site_names_api, -                params={"get_all": "true"}, -                headers=headers -            ) -            cls.snakes = await response.json() - -        # Get the special cases -        if cls.special_cases is None: -            response = await http_session.get( -                URLs.site_special_api, -                headers=headers -            ) -            special_cases = await response.json() -            cls.special_cases = {snake['name'].lower(): snake for snake in special_cases} - -        # Close the session -        http_session.close() - -    @classmethod -    async def random(cls): -        """ -        This is stupid. We should find a way to -        somehow get the global session into a -        global context, so I can get it from here. -        :return: -        """ - -        await cls.build_list() -        names = [snake['scientific'] for snake in cls.snakes] -        return random.choice(names) - -  class ValidPythonIdentifier(Converter):      """      A converter that checks whether the given string is a valid Python identifier. diff --git a/bot/resources/snake_cards/backs/card_back1.jpg b/bot/resources/snake_cards/backs/card_back1.jpgBinary files differ deleted file mode 100644 index 22959fa73..000000000 --- a/bot/resources/snake_cards/backs/card_back1.jpg +++ /dev/null diff --git a/bot/resources/snake_cards/backs/card_back2.jpg b/bot/resources/snake_cards/backs/card_back2.jpgBinary files differ deleted file mode 100644 index d56edc320..000000000 --- a/bot/resources/snake_cards/backs/card_back2.jpg +++ /dev/null diff --git a/bot/resources/snake_cards/card_bottom.png b/bot/resources/snake_cards/card_bottom.pngBinary files differ deleted file mode 100644 index 8b2b91c5c..000000000 --- a/bot/resources/snake_cards/card_bottom.png +++ /dev/null diff --git a/bot/resources/snake_cards/card_frame.png b/bot/resources/snake_cards/card_frame.pngBinary files differ deleted file mode 100644 index 149a0a5f6..000000000 --- a/bot/resources/snake_cards/card_frame.png +++ /dev/null diff --git a/bot/resources/snake_cards/card_top.png b/bot/resources/snake_cards/card_top.pngBinary files differ deleted file mode 100644 index e329c873a..000000000 --- a/bot/resources/snake_cards/card_top.png +++ /dev/null diff --git a/bot/resources/snake_cards/expressway.ttf b/bot/resources/snake_cards/expressway.ttfBinary files differ deleted file mode 100644 index 39e157947..000000000 --- a/bot/resources/snake_cards/expressway.ttf +++ /dev/null diff --git a/bot/resources/snakes_and_ladders/banner.jpg b/bot/resources/snakes_and_ladders/banner.jpgBinary files differ deleted file mode 100644 index 69eaaf129..000000000 --- a/bot/resources/snakes_and_ladders/banner.jpg +++ /dev/null diff --git a/bot/resources/snakes_and_ladders/board.jpg b/bot/resources/snakes_and_ladders/board.jpgBinary files differ deleted file mode 100644 index 20032e391..000000000 --- a/bot/resources/snakes_and_ladders/board.jpg +++ /dev/null diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index 87351eaf3..4c99d50e8 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -1,84 +1,3 @@ -import asyncio -from typing import List - -import discord -from discord.ext.commands import BadArgument, Context - -from bot.pagination import LinePaginator - - -async def disambiguate( -        ctx: Context, entries: List[str], *, timeout: float = 30, -        per_page: int = 20, empty: bool = False, embed: discord.Embed = None -): -    """ -    Has the user choose between multiple entries in case one could not be chosen automatically. - -    This will raise a BadArgument if entries is empty, if the disambiguation event times out, -    or if the user makes an invalid choice. - -    :param ctx: Context object from discord.py -    :param entries: List of items for user to choose from -    :param timeout: Number of seconds to wait before canceling disambiguation -    :param per_page: Entries per embed page -    :param empty: Whether the paginator should have an extra line between items -    :param embed: The embed that the paginator will use. -    :return: Users choice for correct entry. -    """ - -    if len(entries) == 0: -        raise BadArgument('No matches found.') - -    if len(entries) == 1: -        return entries[0] - -    choices = (f'{index}: {entry}' for index, entry in enumerate(entries, start=1)) - -    def check(message): -        return (message.content.isdigit() -                and message.author == ctx.author -                and message.channel == ctx.channel) - -    try: -        if embed is None: -            embed = discord.Embed() - -        coro1 = ctx.bot.wait_for('message', check=check, timeout=timeout) -        coro2 = LinePaginator.paginate(choices, ctx, embed=embed, max_lines=per_page, -                                       empty=empty, max_size=6000, timeout=9000) - -        # wait_for timeout will go to except instead of the wait_for thing as I expected -        futures = [asyncio.ensure_future(coro1), asyncio.ensure_future(coro2)] -        done, pending = await asyncio.wait(futures, return_when=asyncio.FIRST_COMPLETED, loop=ctx.bot.loop) - -        # :yert: -        result = list(done)[0].result() - -        # Pagination was canceled - result is None -        if result is None: -            for coro in pending: -                coro.cancel() -            raise BadArgument('Canceled.') - -        # Pagination was not initiated, only one page -        if result.author == ctx.bot.user: -            # Continue the wait_for -            result = await list(pending)[0] - -        # Love that duplicate code -        for coro in pending: -            coro.cancel() -    except asyncio.TimeoutError: -        raise BadArgument('Timed out.') - -    # Guaranteed to not error because of isdigit() in check -    index = int(result.content) - -    try: -        return entries[index - 1] -    except IndexError: -        raise BadArgument('Invalid choice.') -  class CaseInsensitiveDict(dict):      """ diff --git a/bot/utils/snakes/__init__.py b/bot/utils/snakes/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/bot/utils/snakes/__init__.py +++ /dev/null diff --git a/bot/utils/snakes/hatching.py b/bot/utils/snakes/hatching.py deleted file mode 100644 index b9d29583f..000000000 --- a/bot/utils/snakes/hatching.py +++ /dev/null @@ -1,44 +0,0 @@ -h1 = r'''``` -        ---- -       ------ -     /--------\ -     |--------| -     |--------| -      \------/ -        ----```''' - -h2 = r'''``` -        ---- -       ------ -     /---\-/--\ -     |-----\--| -     |--------| -      \------/ -        ----```''' - -h3 = r'''``` -        ---- -       ------ -     /---\-/--\ -     |-----\--| -     |-----/--| -      \----\-/ -        ----```''' - -h4 = r'''``` -        ----- -       -----  \ -     /--|  /---\ -     |--\  -\---| -     |--\--/--  / -      \------- / -        ------```''' - -stages = [h1, h2, h3, h4] -snakes = { -    "Baby Python": "https://i.imgur.com/SYOcmSa.png", -    "Baby Rattle Snake": "https://i.imgur.com/i5jYA8f.png", -    "Baby Dragon Snake": "https://i.imgur.com/SuMKM4m.png", -    "Baby Garden Snake": "https://i.imgur.com/5vYx3ah.png", -    "Baby Cobra": "https://i.imgur.com/jk14ryt.png" -} diff --git a/bot/utils/snakes/perlin.py b/bot/utils/snakes/perlin.py deleted file mode 100644 index 0401787ef..000000000 --- a/bot/utils/snakes/perlin.py +++ /dev/null @@ -1,158 +0,0 @@ -""" -Perlin noise implementation. -Taken from: https://gist.github.com/eevee/26f547457522755cb1fb8739d0ea89a1 -Licensed under ISC -""" - -import math -import random -from itertools import product - - -def smoothstep(t): -    """Smooth curve with a zero derivative at 0 and 1, making it useful for -    interpolating. -    """ -    return t * t * (3. - 2. * t) - - -def lerp(t, a, b): -    """Linear interpolation between a and b, given a fraction t.""" -    return a + t * (b - a) - - -class PerlinNoiseFactory(object): -    """Callable that produces Perlin noise for an arbitrary point in an -    arbitrary number of dimensions.  The underlying grid is aligned with the -    integers. -    There is no limit to the coordinates used; new gradients are generated on -    the fly as necessary. -    """ - -    def __init__(self, dimension, octaves=1, tile=(), unbias=False): -        """Create a new Perlin noise factory in the given number of dimensions, -        which should be an integer and at least 1. -        More octaves create a foggier and more-detailed noise pattern.  More -        than 4 octaves is rather excessive. -        ``tile`` can be used to make a seamlessly tiling pattern.  For example: -            pnf = PerlinNoiseFactory(2, tile=(0, 3)) -        This will produce noise that tiles every 3 units vertically, but never -        tiles horizontally. -        If ``unbias`` is true, the smoothstep function will be applied to the -        output before returning it, to counteract some of Perlin noise's -        significant bias towards the center of its output range. -        """ -        self.dimension = dimension -        self.octaves = octaves -        self.tile = tile + (0,) * dimension -        self.unbias = unbias - -        # For n dimensions, the range of Perlin noise is ±sqrt(n)/2; multiply -        # by this to scale to ±1 -        self.scale_factor = 2 * dimension ** -0.5 - -        self.gradient = {} - -    def _generate_gradient(self): -        # Generate a random unit vector at each grid point -- this is the -        # "gradient" vector, in that the grid tile slopes towards it - -        # 1 dimension is special, since the only unit vector is trivial; -        # instead, use a slope between -1 and 1 -        if self.dimension == 1: -            return (random.uniform(-1, 1),) - -        # Generate a random point on the surface of the unit n-hypersphere; -        # this is the same as a random unit vector in n dimensions.  Thanks -        # to: http://mathworld.wolfram.com/SpherePointPicking.html -        # Pick n normal random variables with stddev 1 -        random_point = [random.gauss(0, 1) for _ in range(self.dimension)] -        # Then scale the result to a unit vector -        scale = sum(n * n for n in random_point) ** -0.5 -        return tuple(coord * scale for coord in random_point) - -    def get_plain_noise(self, *point): -        """Get plain noise for a single point, without taking into account -        either octaves or tiling. -        """ -        if len(point) != self.dimension: -            raise ValueError("Expected {0} values, got {1}".format( -                self.dimension, len(point))) - -        # Build a list of the (min, max) bounds in each dimension -        grid_coords = [] -        for coord in point: -            min_coord = math.floor(coord) -            max_coord = min_coord + 1 -            grid_coords.append((min_coord, max_coord)) - -        # Compute the dot product of each gradient vector and the point's -        # distance from the corresponding grid point.  This gives you each -        # gradient's "influence" on the chosen point. -        dots = [] -        for grid_point in product(*grid_coords): -            if grid_point not in self.gradient: -                self.gradient[grid_point] = self._generate_gradient() -            gradient = self.gradient[grid_point] - -            dot = 0 -            for i in range(self.dimension): -                dot += gradient[i] * (point[i] - grid_point[i]) -            dots.append(dot) - -        # Interpolate all those dot products together.  The interpolation is -        # done with smoothstep to smooth out the slope as you pass from one -        # grid cell into the next. -        # Due to the way product() works, dot products are ordered such that -        # the last dimension alternates: (..., min), (..., max), etc.  So we -        # can interpolate adjacent pairs to "collapse" that last dimension.  Then -        # the results will alternate in their second-to-last dimension, and so -        # forth, until we only have a single value left. -        dim = self.dimension -        while len(dots) > 1: -            dim -= 1 -            s = smoothstep(point[dim] - grid_coords[dim][0]) - -            next_dots = [] -            while dots: -                next_dots.append(lerp(s, dots.pop(0), dots.pop(0))) - -            dots = next_dots - -        return dots[0] * self.scale_factor - -    def __call__(self, *point): -        """Get the value of this Perlin noise function at the given point.  The -        number of values given should match the number of dimensions. -        """ -        ret = 0 -        for o in range(self.octaves): -            o2 = 1 << o -            new_point = [] -            for i, coord in enumerate(point): -                coord *= o2 -                if self.tile[i]: -                    coord %= self.tile[i] * o2 -                new_point.append(coord) -            ret += self.get_plain_noise(*new_point) / o2 - -        # Need to scale n back down since adding all those extra octaves has -        # probably expanded it beyond ±1 -        # 1 octave: ±1 -        # 2 octaves: ±1½ -        # 3 octaves: ±1¾ -        ret /= 2 - 2 ** (1 - self.octaves) - -        if self.unbias: -            # The output of the plain Perlin noise algorithm has a fairly -            # strong bias towards the center due to the central limit theorem -            # -- in fact the top and bottom 1/8 virtually never happen.  That's -            # a quarter of our entire output range!  If only we had a function -            # in [0..1] that could introduce a bias towards the endpoints... -            r = (ret + 1) / 2 -            # Doing it this many times is a completely made-up heuristic. -            for _ in range(int(self.octaves / 2 + 0.5)): -                r = smoothstep(r) -            ret = r * 2 - 1 - -        return ret diff --git a/bot/utils/snakes/perlinsneks.py b/bot/utils/snakes/perlinsneks.py deleted file mode 100644 index 662281775..000000000 --- a/bot/utils/snakes/perlinsneks.py +++ /dev/null @@ -1,111 +0,0 @@ -# perlin sneks! -import io -import math -import random -from typing import Tuple - -from PIL.ImageDraw import Image, ImageDraw - -from bot.utils.snakes import perlin - -DEFAULT_SNAKE_COLOR: int = 0x15c7ea -DEFAULT_BACKGROUND_COLOR: int = 0 -DEFAULT_IMAGE_DIMENSIONS: Tuple[int] = (200, 200) -DEFAULT_SNAKE_LENGTH: int = 22 -DEFAULT_SNAKE_WIDTH: int = 8 -DEFAULT_SEGMENT_LENGTH_RANGE: Tuple[int] = (7, 10) -DEFAULT_IMAGE_MARGINS: Tuple[int] = (50, 50) -DEFAULT_TEXT: str = "snek\nit\nup" -DEFAULT_TEXT_POSITION: Tuple[int] = ( -    10, -    10 -) -DEFAULT_TEXT_COLOR: int = 0xf2ea15 - -X = 0 -Y = 1 -ANGLE_RANGE = math.pi * 2 - - -def create_snek_frame( -        perlin_factory: perlin.PerlinNoiseFactory, perlin_lookup_vertical_shift: float = 0, -        image_dimensions: Tuple[int] = DEFAULT_IMAGE_DIMENSIONS, image_margins: Tuple[int] = DEFAULT_IMAGE_MARGINS, -        snake_length: int = DEFAULT_SNAKE_LENGTH, -        snake_color: int = DEFAULT_SNAKE_COLOR, bg_color: int = DEFAULT_BACKGROUND_COLOR, -        segment_length_range: Tuple[int] = DEFAULT_SEGMENT_LENGTH_RANGE, snake_width: int = DEFAULT_SNAKE_WIDTH, -        text: str = DEFAULT_TEXT, text_position: Tuple[int] = DEFAULT_TEXT_POSITION, -        text_color: Tuple[int] = DEFAULT_TEXT_COLOR -) -> Image: -    """ -    Creates a single random snek frame using Perlin noise. -    :param perlin_factory: the perlin noise factory used. Required. -    :param perlin_lookup_vertical_shift: the Perlin noise shift in the Y-dimension for this frame -    :param image_dimensions: the size of the output image. -    :param image_margins: the margins to respect inside of the image. -    :param snake_length: the length of the snake, in segments. -    :param snake_color: the color of the snake. -    :param bg_color: the background color. -    :param segment_length_range: the range of the segment length. Values will be generated inside this range, including -                                 the bounds. -    :param snake_width: the width of the snek, in pixels. -    :param text: the text to display with the snek. Set to None for no text. -    :param text_position: the position of the text. -    :param text_color: the color of the text. -    :return: a PIL image, representing a single frame. -    """ -    start_x = random.randint(image_margins[X], image_dimensions[X] - image_margins[X]) -    start_y = random.randint(image_margins[Y], image_dimensions[Y] - image_margins[Y]) -    points = [(start_x, start_y)] - -    for index in range(0, snake_length): -        angle = perlin_factory.get_plain_noise( -            ((1 / (snake_length + 1)) * (index + 1)) + perlin_lookup_vertical_shift -        ) * ANGLE_RANGE -        current_point = points[index] -        segment_length = random.randint(segment_length_range[0], segment_length_range[1]) -        points.append(( -            current_point[X] + segment_length * math.cos(angle), -            current_point[Y] + segment_length * math.sin(angle) -        )) - -    # normalize bounds -    min_dimensions = [start_x, start_y] -    max_dimensions = [start_x, start_y] -    for point in points: -        min_dimensions[X] = min(point[X], min_dimensions[X]) -        min_dimensions[Y] = min(point[Y], min_dimensions[Y]) -        max_dimensions[X] = max(point[X], max_dimensions[X]) -        max_dimensions[Y] = max(point[Y], max_dimensions[Y]) - -    # shift towards middle -    dimension_range = (max_dimensions[X] - min_dimensions[X], max_dimensions[Y] - min_dimensions[Y]) -    shift = ( -        image_dimensions[X] / 2 - (dimension_range[X] / 2 + min_dimensions[X]), -        image_dimensions[Y] / 2 - (dimension_range[Y] / 2 + min_dimensions[Y]) -    ) - -    image = Image.new(mode='RGB', size=image_dimensions, color=bg_color) -    draw = ImageDraw(image) -    for index in range(1, len(points)): -        point = points[index] -        previous = points[index - 1] -        draw.line( -            ( -                shift[X] + previous[X], -                shift[Y] + previous[Y], -                shift[X] + point[X], -                shift[Y] + point[Y] -            ), -            width=snake_width, -            fill=snake_color -        ) -    if text is not None: -        draw.multiline_text(text_position, text, fill=text_color) -    del draw -    return image - - -def frame_to_png_bytes(image: Image): -    stream = io.BytesIO() -    image.save(stream, format='PNG') -    return stream.getvalue() diff --git a/bot/utils/snakes/sal.py b/bot/utils/snakes/sal.py deleted file mode 100644 index 8530d8a0f..000000000 --- a/bot/utils/snakes/sal.py +++ /dev/null @@ -1,365 +0,0 @@ -import asyncio -import io -import logging -import math -import os -import random - -import aiohttp -from discord import File, Member, Reaction -from discord.ext.commands import Context -from PIL import Image - -from bot.utils.snakes.sal_board import ( -    BOARD, BOARD_MARGIN, BOARD_PLAYER_SIZE, -    BOARD_TILE_SIZE, MAX_PLAYERS, PLAYER_ICON_IMAGE_SIZE -) - -log = logging.getLogger(__name__) - -# Emoji constants -START_EMOJI = "\u2611"     # :ballot_box_with_check: - Start the game -CANCEL_EMOJI = "\u274C"    # :x: - Cancel or leave the game -ROLL_EMOJI = "\U0001F3B2"  # :game_die: - Roll the die! -JOIN_EMOJI = "\U0001F64B"  # :raising_hand: - Join the game. - -STARTUP_SCREEN_EMOJI = [ -    JOIN_EMOJI, -    START_EMOJI, -    CANCEL_EMOJI -] - -GAME_SCREEN_EMOJI = [ -    ROLL_EMOJI, -    CANCEL_EMOJI -] - - -class SnakeAndLaddersGame: -    def __init__(self, snakes, context: Context): -        self.snakes = snakes -        self.ctx = context -        self.channel = self.ctx.channel -        self.state = 'booting' -        self.started = False -        self.author = self.ctx.author -        self.players = [] -        self.player_tiles = {} -        self.round_has_rolled = {} -        self.avatar_images = {} -        self.board = None -        self.positions = None -        self.rolls = [] - -    async def open_game(self): -        """ -        Create a new Snakes and Ladders game. - -        Listen for reactions until players have joined, -        and the game has been started. -        """ - -        def startup_event_check(reaction_: Reaction, user_: Member): -            """ -            Make sure that this reaction is what we want to operate on -            """ -            return ( -                all(( -                    reaction_.message.id == startup.id,       # Reaction is on startup message -                    reaction_.emoji in STARTUP_SCREEN_EMOJI,  # Reaction is one of the startup emotes -                    user_.id != self.ctx.bot.user.id,         # Reaction was not made by the bot -                )) -            ) - -        # Check to see if the bot can remove reactions -        if not self.channel.permissions_for(self.ctx.guild.me).manage_messages: -            log.warning( -                "Unable to start Snakes and Ladders - " -                f"Missing manage_messages permissions in {self.channel}" -            ) -            return - -        await self._add_player(self.author) -        await self.channel.send( -            "**Snakes and Ladders**: A new game is about to start!", -            file=File( -                os.path.join("bot", "resources", "snakes_and_ladders", "banner.jpg"), -                filename='Snakes and Ladders.jpg' -            ) -        ) -        startup = await self.channel.send( -            f"Press {JOIN_EMOJI} to participate, and press " -            f"{START_EMOJI} to start the game" -        ) -        for emoji in STARTUP_SCREEN_EMOJI: -            await startup.add_reaction(emoji) - -        self.state = 'waiting' - -        while not self.started: -            try: -                reaction, user = await self.ctx.bot.wait_for( -                    "reaction_add", -                    timeout=300, -                    check=startup_event_check -                ) -                if reaction.emoji == JOIN_EMOJI: -                    await self.player_join(user) -                elif reaction.emoji == CANCEL_EMOJI: -                    if self.ctx.author == user: -                        await self.cancel_game(user) -                        return -                    else: -                        await self.player_leave(user) -                elif reaction.emoji == START_EMOJI: -                    if self.ctx.author == user: -                        self.started = True -                        await self.start_game(user) -                        await startup.delete() -                        break - -                await startup.remove_reaction(reaction.emoji, user) - -            except asyncio.TimeoutError: -                log.debug("Snakes and Ladders timed out waiting for a reaction") -                self.cancel_game(self.author) -                return  # We're done, no reactions for the last 5 minutes - -    async def _add_player(self, user: Member): -        self.players.append(user) -        self.player_tiles[user.id] = 1 -        avatar_url = user.avatar_url_as(format='jpeg', size=PLAYER_ICON_IMAGE_SIZE) -        async with aiohttp.ClientSession() as session: -            async with session.get(avatar_url) as res: -                avatar_bytes = await res.read() -                im = Image.open(io.BytesIO(avatar_bytes)).resize((BOARD_PLAYER_SIZE, BOARD_PLAYER_SIZE)) -                self.avatar_images[user.id] = im - -    async def player_join(self, user: Member): -        for p in self.players: -            if user == p: -                await self.channel.send(user.mention + " You are already in the game.", delete_after=10) -                return -        if self.state != 'waiting': -            await self.channel.send(user.mention + " You cannot join at this time.", delete_after=10) -            return -        if len(self.players) is MAX_PLAYERS: -            await self.channel.send(user.mention + " The game is full!", delete_after=10) -            return - -        await self._add_player(user) - -        await self.channel.send( -            f"**Snakes and Ladders**: {user.mention} has joined the game.\n" -            f"There are now {str(len(self.players))} players in the game.", -            delete_after=10 -        ) - -    async def player_leave(self, user: Member): -        if user == self.author: -            await self.channel.send( -                user.mention + " You are the author, and cannot leave the game. Execute " -                "`sal cancel` to cancel the game.", -                delete_after=10 -            ) -            return -        for p in self.players: -            if user == p: -                self.players.remove(p) -                self.player_tiles.pop(p.id, None) -                self.round_has_rolled.pop(p.id, None) -                await self.channel.send( -                    "**Snakes and Ladders**: " + user.mention + " has left the game.", -                    delete_after=10 -                ) - -                if self.state != 'waiting' and len(self.players) == 1: -                    await self.channel.send("**Snakes and Ladders**: The game has been surrendered!") -                    self._destruct() -                return -        await self.channel.send(user.mention + " You are not in the match.", delete_after=10) - -    async def cancel_game(self, user: Member): -        if not user == self.author: -            await self.channel.send(user.mention + " Only the author of the game can cancel it.", delete_after=10) -            return -        await self.channel.send("**Snakes and Ladders**: Game has been canceled.") -        self._destruct() - -    async def start_game(self, user: Member): -        if not user == self.author: -            await self.channel.send(user.mention + " Only the author of the game can start it.", delete_after=10) -            return -        if len(self.players) < 1: -            await self.channel.send( -                user.mention + " A minimum of 2 players is required to start the game.", -                delete_after=10 -            ) -            return -        if not self.state == 'waiting': -            await self.channel.send(user.mention + " The game cannot be started at this time.", delete_after=10) -            return -        self.state = 'starting' -        player_list = ', '.join(user.mention for user in self.players) -        await self.channel.send("**Snakes and Ladders**: The game is starting!\nPlayers: " + player_list) -        await self.start_round() - -    async def start_round(self): - -        def game_event_check(reaction_: Reaction, user_: Member): -            """ -            Make sure that this reaction is what we want to operate on -            """ -            return ( -                all(( -                    reaction_.message.id == self.positions.id,  # Reaction is on positions message -                    reaction_.emoji in GAME_SCREEN_EMOJI,       # Reaction is one of the game emotes -                    user_.id != self.ctx.bot.user.id,           # Reaction was not made by the bot -                )) -            ) - -        self.state = 'roll' -        for user in self.players: -            self.round_has_rolled[user.id] = False -        board_img = Image.open(os.path.join("bot", "resources", "snakes_and_ladders", "board.jpg")) -        player_row_size = math.ceil(MAX_PLAYERS / 2) - -        for i, player in enumerate(self.players): -            tile = self.player_tiles[player.id] -            tile_coordinates = self._board_coordinate_from_index(tile) -            x_offset = BOARD_MARGIN[0] + tile_coordinates[0] * BOARD_TILE_SIZE -            y_offset = \ -                BOARD_MARGIN[1] + ( -                    (10 * BOARD_TILE_SIZE) - (9 - tile_coordinates[1]) * BOARD_TILE_SIZE - BOARD_PLAYER_SIZE) -            x_offset += BOARD_PLAYER_SIZE * (i % player_row_size) -            y_offset -= BOARD_PLAYER_SIZE * math.floor(i / player_row_size) -            board_img.paste(self.avatar_images[player.id], -                            box=(x_offset, y_offset)) -        stream = io.BytesIO() -        board_img.save(stream, format='JPEG') -        board_file = File(stream.getvalue(), filename='Board.jpg') -        player_list = '\n'.join((user.mention + ": Tile " + str(self.player_tiles[user.id])) for user in self.players) - -        # Store and send new messages -        temp_board = await self.channel.send( -            "**Snakes and Ladders**: A new round has started! Current board:", -            file=board_file -        ) -        temp_positions = await self.channel.send( -            f"**Current positions**:\n{player_list}\n\nUse {ROLL_EMOJI} to roll the dice!" -        ) - -        # Delete the previous messages -        if self.board and self.positions: -            await self.board.delete() -            await self.positions.delete() - -        # remove the roll messages -        for roll in self.rolls: -            await roll.delete() -        self.rolls = [] - -        # Save new messages -        self.board = temp_board -        self.positions = temp_positions - -        # Wait for rolls -        for emoji in GAME_SCREEN_EMOJI: -            await self.positions.add_reaction(emoji) - -        while True: -            try: -                reaction, user = await self.ctx.bot.wait_for( -                    "reaction_add", -                    timeout=300, -                    check=game_event_check -                ) - -                if reaction.emoji == ROLL_EMOJI: -                    await self.player_roll(user) -                elif reaction.emoji == CANCEL_EMOJI: -                    if self.ctx.author == user: -                        await self.cancel_game(user) -                        return -                    else: -                        await self.player_leave(user) - -                await self.positions.remove_reaction(reaction.emoji, user) - -                if self._check_all_rolled(): -                    break - -            except asyncio.TimeoutError: -                log.debug("Snakes and Ladders timed out waiting for a reaction") -                await self.cancel_game(self.author) -                return  # We're done, no reactions for the last 5 minutes - -        # Round completed -        await self._complete_round() - -    async def player_roll(self, user: Member): -        if user.id not in self.player_tiles: -            await self.channel.send(user.mention + " You are not in the match.", delete_after=10) -            return -        if self.state != 'roll': -            await self.channel.send(user.mention + " You may not roll at this time.", delete_after=10) -            return -        if self.round_has_rolled[user.id]: -            return -        roll = random.randint(1, 6) -        self.rolls.append(await self.channel.send(f"{user.mention} rolled a **{roll}**!")) -        next_tile = self.player_tiles[user.id] + roll - -        # apply snakes and ladders -        if next_tile in BOARD: -            target = BOARD[next_tile] -            if target < next_tile: -                await self.channel.send( -                    f"{user.mention} slips on a snake and falls back to **{target}**", -                    delete_after=15 -                ) -            else: -                await self.channel.send( -                    f"{user.mention} climbs a ladder to **{target}**", -                    delete_after=15 -                ) -            next_tile = target - -        self.player_tiles[user.id] = min(100, next_tile) -        self.round_has_rolled[user.id] = True - -    async def _complete_round(self): - -        self.state = 'post_round' - -        # check for winner -        winner = self._check_winner() -        if winner is None: -            # there is no winner, start the next round -            await self.start_round() -            return - -        # announce winner and exit -        await self.channel.send("**Snakes and Ladders**: " + winner.mention + " has won the game! :tada:") -        self._destruct() - -    def _check_winner(self) -> Member: -        if self.state != 'post_round': -            return None -        return next((player for player in self.players if self.player_tiles[player.id] == 100), -                    None) - -    def _check_all_rolled(self): -        return all(rolled for rolled in self.round_has_rolled.values()) - -    def _destruct(self): -        del self.snakes.active_sal[self.channel] - -    def _board_coordinate_from_index(self, index: int): -        # converts the tile number to the x/y coordinates for graphical purposes -        y_level = 9 - math.floor((index - 1) / 10) -        is_reversed = math.floor((index - 1) / 10) % 2 != 0 -        x_level = (index - 1) % 10 -        if is_reversed: -            x_level = 9 - x_level -        return x_level, y_level diff --git a/bot/utils/snakes/sal_board.py b/bot/utils/snakes/sal_board.py deleted file mode 100644 index 1b8eab451..000000000 --- a/bot/utils/snakes/sal_board.py +++ /dev/null @@ -1,33 +0,0 @@ -BOARD_TILE_SIZE = 56         # the size of each board tile -BOARD_PLAYER_SIZE = 20       # the size of each player icon -BOARD_MARGIN = (10, 0)       # margins, in pixels (for player icons) -PLAYER_ICON_IMAGE_SIZE = 32  # the size of the image to download, should a power of 2 and higher than BOARD_PLAYER_SIZE -MAX_PLAYERS = 4              # depends on the board size/quality, 4 is for the default board - -# board definition (from, to) -BOARD = { -    # ladders -    2: 38, -    7: 14, -    8: 31, -    15: 26, -    21: 42, -    28: 84, -    36: 44, -    51: 67, -    71: 91, -    78: 98, -    87: 94, - -    # snakes -    99: 80, -    95: 75, -    92: 88, -    89: 68, -    74: 53, -    64: 60, -    62: 19, -    49: 11, -    46: 25, -    16: 6 -} diff --git a/config-default.yml b/config-default.yml index 50bea189b..e5681dc3d 100644 --- a/config-default.yml +++ b/config-default.yml @@ -222,9 +222,7 @@ filter:  keys:      deploy_bot:  !ENV "DEPLOY_BOT_KEY"      deploy_site: !ENV "DEPLOY_SITE" -    omdb:        !ENV "OMDB_API_KEY"      site_api:    !ENV "BOT_API_KEY" -    youtube:     !ENV "YOUTUBE_API_KEY"  rabbitmq: @@ -243,9 +241,7 @@ urls:      site_bigbrother_api:                !JOIN [*SCHEMA, *API, "/bot/bigbrother"]      site_docs_api:                      !JOIN [*SCHEMA, *API, "/bot/docs"] -    site_facts_api:                     !JOIN [*SCHEMA, *API, "/bot/snake_facts"]      site_superstarify_api:              !JOIN [*SCHEMA, *API, "/bot/superstarify"] -    site_idioms_api:                    !JOIN [*SCHEMA, *API, "/bot/snake_idioms"]      site_infractions:                   !JOIN [*SCHEMA, *API, "/bot/infractions"]      site_infractions_user:              !JOIN [*SCHEMA, *API, "/bot/infractions/user/{user_id}"]      site_infractions_type:              !JOIN [*SCHEMA, *API, "/bot/infractions/type/{infraction_type}"] @@ -254,13 +250,10 @@ urls:      site_infractions_user_type:         !JOIN [*SCHEMA, *API, "/bot/infractions/user/{user_id}/{infraction_type}"]      site_logs_api:                      !JOIN [*SCHEMA, *API, "/bot/logs"]      site_logs_view:                     !JOIN [*SCHEMA, *DOMAIN, "/bot/logs"] -    site_names_api:                     !JOIN [*SCHEMA, *API, "/bot/snake_names"]      site_off_topic_names_api:           !JOIN [*SCHEMA, *API, "/bot/off-topic-names"] -    site_quiz_api:                      !JOIN [*SCHEMA, *API, "/bot/snake_quiz"]      site_reminders_api:                 !JOIN [*SCHEMA, *API, "/bot/reminders"]      site_reminders_user_api:            !JOIN [*SCHEMA, *API, "/bot/reminders/user"]      site_settings_api:                  !JOIN [*SCHEMA, *API, "/bot/settings"] -    site_special_api:                   !JOIN [*SCHEMA, *API, "/bot/special_snakes"]      site_tags_api:                      !JOIN [*SCHEMA, *API, "/bot/tags"]      site_user_api:                      !JOIN [*SCHEMA, *API, "/bot/users"]      site_user_complete_api:             !JOIN [*SCHEMA, *API, "/bot/users/complete"] @@ -277,7 +270,6 @@ urls:      # Misc URLs      bot_avatar:      "https://raw.githubusercontent.com/discord-python/branding/master/logos/logo_circle/logo_circle.png"      gitlab_bot_repo: "https://gitlab.com/python-discord/projects/bot" -    omdb:            "http://omdbapi.com"  anti_spam:      # Clean messages that violate a rule. | 
