aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar scragly <[email protected]>2019-03-07 17:06:05 +1000
committerGravatar GitHub <[email protected]>2019-03-07 17:06:05 +1000
commit4555d90b60eedbb6a3032512277f307d69034f6f (patch)
treef00244b5dc91369a418ac7b52512f7d554216c4c
parentUpdate config-default.yml (diff)
parentFix linting issues (diff)
Merge pull request #328 from python-discord/remove-snake-cog
Remove the "Snakes" cog
-rw-r--r--Pipfile1
-rw-r--r--Pipfile.lock476
-rw-r--r--bot/__init__.py1
-rw-r--r--bot/__main__.py1
-rw-r--r--bot/cogs/events.py11
-rw-r--r--bot/cogs/snakes.py1216
-rw-r--r--bot/constants.py8
-rw-r--r--bot/converters.py105
-rw-r--r--bot/resources/snake_cards/backs/card_back1.jpgbin165788 -> 0 bytes
-rw-r--r--bot/resources/snake_cards/backs/card_back2.jpgbin140868 -> 0 bytes
-rw-r--r--bot/resources/snake_cards/card_bottom.pngbin18165 -> 0 bytes
-rw-r--r--bot/resources/snake_cards/card_frame.pngbin1460 -> 0 bytes
-rw-r--r--bot/resources/snake_cards/card_top.pngbin12581 -> 0 bytes
-rw-r--r--bot/resources/snake_cards/expressway.ttfbin156244 -> 0 bytes
-rw-r--r--bot/resources/snakes_and_ladders/banner.jpgbin17928 -> 0 bytes
-rw-r--r--bot/resources/snakes_and_ladders/board.jpgbin80264 -> 0 bytes
-rw-r--r--bot/utils/__init__.py81
-rw-r--r--bot/utils/snakes/__init__.py0
-rw-r--r--bot/utils/snakes/hatching.py44
-rw-r--r--bot/utils/snakes/perlin.py158
-rw-r--r--bot/utils/snakes/perlinsneks.py111
-rw-r--r--bot/utils/snakes/sal.py365
-rw-r--r--bot/utils/snakes/sal_board.py33
-rw-r--r--config-default.yml8
24 files changed, 234 insertions, 2385 deletions
diff --git a/Pipfile b/Pipfile
index 3df5ea7c0..45b013076 100644
--- a/Pipfile
+++ b/Pipfile
@@ -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/events.py b/bot/cogs/events.py
index 8dac83d9b..2819b7dcc 100644
--- a/bot/cogs/events.py
+++ b/bot/cogs/events.py
@@ -1,5 +1,6 @@
import logging
from functools import partial
+from typing import List
from discord import Colour, Embed, Member, Object
from discord.ext.commands import (
@@ -175,7 +176,7 @@ class Events:
users = []
for member in self.bot.get_guild(Guild.id).members: # type: Member
- roles = [str(r.id) for r in member.roles] # type: List[int]
+ roles: List[int] = [str(r.id) for r in member.roles]
users.append({
"avatar": member.avatar_url_as(format="png"),
@@ -225,9 +226,9 @@ class Events:
and before.avatar == after.avatar):
return
- before_role_names = [role.name for role in before.roles] # type: List[str]
- after_role_names = [role.name for role in after.roles] # type: List[str]
- role_ids = [str(r.id) for r in after.roles] # type: List[str]
+ before_role_names: List[str] = [role.name for role in before.roles]
+ after_role_names: List[str] = [role.name for role in after.roles]
+ role_ids: List[str] = [str(r.id) for r in after.roles]
log.debug(f"{before.display_name} roles changing from {before_role_names} to {after_role_names}")
@@ -242,7 +243,7 @@ class Events:
log.debug(f"User {after.id} updated; changes: {changes}")
async def on_member_join(self, member: Member):
- role_ids = [str(r.id) for r in member.roles] # type: List[str]
+ role_ids: List[str] = [str(r.id) for r in member.roles]
new_roles = []
try:
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.jpg
deleted file mode 100644
index 22959fa73..000000000
--- a/bot/resources/snake_cards/backs/card_back1.jpg
+++ /dev/null
Binary files differ
diff --git a/bot/resources/snake_cards/backs/card_back2.jpg b/bot/resources/snake_cards/backs/card_back2.jpg
deleted file mode 100644
index d56edc320..000000000
--- a/bot/resources/snake_cards/backs/card_back2.jpg
+++ /dev/null
Binary files differ
diff --git a/bot/resources/snake_cards/card_bottom.png b/bot/resources/snake_cards/card_bottom.png
deleted file mode 100644
index 8b2b91c5c..000000000
--- a/bot/resources/snake_cards/card_bottom.png
+++ /dev/null
Binary files differ
diff --git a/bot/resources/snake_cards/card_frame.png b/bot/resources/snake_cards/card_frame.png
deleted file mode 100644
index 149a0a5f6..000000000
--- a/bot/resources/snake_cards/card_frame.png
+++ /dev/null
Binary files differ
diff --git a/bot/resources/snake_cards/card_top.png b/bot/resources/snake_cards/card_top.png
deleted file mode 100644
index e329c873a..000000000
--- a/bot/resources/snake_cards/card_top.png
+++ /dev/null
Binary files differ
diff --git a/bot/resources/snake_cards/expressway.ttf b/bot/resources/snake_cards/expressway.ttf
deleted file mode 100644
index 39e157947..000000000
--- a/bot/resources/snake_cards/expressway.ttf
+++ /dev/null
Binary files differ
diff --git a/bot/resources/snakes_and_ladders/banner.jpg b/bot/resources/snakes_and_ladders/banner.jpg
deleted file mode 100644
index 69eaaf129..000000000
--- a/bot/resources/snakes_and_ladders/banner.jpg
+++ /dev/null
Binary files differ
diff --git a/bot/resources/snakes_and_ladders/board.jpg b/bot/resources/snakes_and_ladders/board.jpg
deleted file mode 100644
index 20032e391..000000000
--- a/bot/resources/snakes_and_ladders/board.jpg
+++ /dev/null
Binary files differ
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.