diff --git a/.travis.yml b/.travis.yml index 2d5a34fd6..2d83eccff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ jobs: python: 3.6.6 env: - DEPLOYING=true + - TOXENV=py36 deploy: - provider: pypi user: Red-DiscordBot @@ -49,12 +50,13 @@ jobs: python: 3.6.6 env: - DEPLOYING=true + - TOXENV=py36 before_deploy: - curl https://artifacts.crowdin.com/repo/GPG-KEY-crowdin | sudo apt-key add - - echo "deb https://artifacts.crowdin.com/repo/deb/ /" | sudo tee -a /etc/apt/sources.list - sudo apt-get update -qq - sudo apt-get install -y crowdin - - pip install redgettext==2.1 + - pip install redgettext==2.2 deploy: - provider: script script: make gettext diff --git a/Pipfile.lock b/Pipfile.lock index cd486bdf6..69d5be9a3 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -57,11 +57,10 @@ }, "async-timeout": { "hashes": [ - "sha256:474d4bc64cee20603e225eb1ece15e248962958b45a3648a9f5cc29e827a610c", - "sha256:b3c0ddc416736619bd4a95ca31de8da6920c3b9a140c64dbef2b2fa7bf521287" + "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", + "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" ], - "markers": "python_version >= '3.5.3'", - "version": "==3.0.0" + "version": "==3.0.1" }, "attrs": { "hashes": [ @@ -79,15 +78,20 @@ }, "colorama": { "hashes": [ - "sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", - "sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1" + "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", + "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" ], - "version": "==0.3.9" + "version": "==0.4.1" + }, + "discord-py": { + "editable": true, + "git": "git://github.com/Rapptz/discord.py", + "ref": "7f4c57dd5ad20b7fa10aea485f674a4bc24b9547" }, "discord.py": { "editable": true, "git": "git://github.com/Rapptz/discord.py", - "ref": "836ae730401ea370aa10127bb9c86854c8b516ac" + "ref": "rewrite" }, "distro": { "hashes": [ @@ -98,10 +102,10 @@ }, "dnspython": { "hashes": [ - "sha256:40f563e1f7a7b80dc5a4e76ad75c23da53d62f1e15e6e517293b04e1f84ead7c", - "sha256:861e6e58faa730f9845aaaa9c6c832851fbf89382ac52915a51f89c71accdd31" + "sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01", + "sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d" ], - "version": "==1.15.0" + "version": "==1.16.0" }, "e1839a8": { "editable": true, @@ -120,10 +124,10 @@ }, "idna": { "hashes": [ - "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", - "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" ], - "version": "==2.7" + "version": "==2.8" }, "idna-ssl": { "hashes": [ @@ -140,72 +144,76 @@ }, "multidict": { "hashes": [ - "sha256:05eeab69bf2b0664644c62bd92fabb045163e5b8d4376a31dfb52ce0210ced7b", - "sha256:0c85880efa7cadb18e3b5eef0aa075dc9c0a3064cbbaef2e20be264b9cf47a64", - "sha256:136f5a4a6a4adeacc4dc820b8b22f0a378fb74f326e259c54d1817639d1d40a0", - "sha256:14906ad3347c7d03e9101749b16611cf2028547716d0840838d3c5e2b3b0f2d3", - "sha256:1ade4a3b71b1bf9e90c5f3d034a87fe4949c087ef1f6cd727fdd766fe8bbd121", - "sha256:22939a00a511a59f9ecc0158b8db728afef57975ce3782b3a265a319d05b9b12", - "sha256:2b86b02d872bc5ba5b3a4530f6a7ba0b541458ab4f7c1429a12ac326231203f7", - "sha256:3c11e92c3dfc321014e22fb442bc9eb70e01af30d6ce442026b0c35723448c66", - "sha256:4ba3bd26f282b201fdbce351f1c5d17ceb224cbedb73d6e96e6ce391b354aacc", - "sha256:4c6e78d042e93751f60672989efbd6a6bc54213ed7ff695fff82784bbb9ea035", - "sha256:4d80d1901b89cc935a6cf5b9fd89df66565272722fe2e5473168927a9937e0ca", - "sha256:4fcf71d33178a00cc34a57b29f5dab1734b9ce0f1c97fb34666deefac6f92037", - "sha256:52f7670b41d4b4d97866ebc38121de8bcb9813128b7c4942b07794d08193c0ab", - "sha256:5368e2b7649a26b7253c6c9e53241248aab9da49099442f5be238fde436f18c9", - "sha256:5bb65fbb48999044938f0c0508e929b14a9b8bf4939d8263e9ea6691f7b54663", - "sha256:60672bb5577472800fcca1ac9dae232d1461db9f20f055184be8ce54b0052572", - "sha256:669e9be6d148fc0283f53e17dd140cde4dc7c87edac8319147edd5aa2a830771", - "sha256:6a0b7a804e8d1716aa2c72e73210b48be83d25ba9ec5cf52cf91122285707bb1", - "sha256:79034ea3da3cf2a815e3e52afdc1f6c1894468c98bdce5d2546fa2342585497f", - "sha256:79247feeef6abcc11137ad17922e865052f23447152059402fc320f99ff544bb", - "sha256:81671c2049e6bf42c7fd11a060f8bc58f58b7b3d6f3f951fc0b15e376a6a5a98", - "sha256:82ac4a5cb56cc9280d4ae52c2d2ebcd6e0668dd0f9ef17f0a9d7c82bd61e24fa", - "sha256:9436267dbbaa49dad18fbbb54f85386b0f5818d055e7b8e01d219661b6745279", - "sha256:94e4140bb1343115a1afd6d84ebf8fca5fb7bfb50e1c2cbd6f2fb5d3117ef102", - "sha256:a2cab366eae8a0ffe0813fd8e335cf0d6b9bb6c5227315f53bb457519b811537", - "sha256:a596019c3eafb1b0ae07db9f55a08578b43c79adb1fe1ab1fd818430ae59ee6f", - "sha256:e8848ae3cd6a784c29fae5055028bee9bffcc704d8bcad09bd46b42b44a833e2", - "sha256:e8a048bfd7d5a280f27527d11449a509ddedf08b58a09a24314828631c099306", - "sha256:f6dd28a0ac60e2426a6918f36f1b4e2620fc785a0de7654cd206ba842eee57fd" + "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f", + "sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3", + "sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef", + "sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b", + "sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73", + "sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc", + "sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3", + "sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd", + "sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351", + "sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941", + "sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d", + "sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1", + "sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b", + "sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a", + "sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3", + "sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7", + "sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0", + "sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0", + "sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014", + "sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5", + "sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036", + "sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d", + "sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a", + "sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce", + "sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1", + "sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a", + "sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9", + "sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7", + "sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b" ], - "version": "==4.4.2" + "version": "==4.5.2" }, "pymongo": { "hashes": [ - "sha256:08dea6dbff33363419af7af3bf2e9a373ff71eb22833dd7063f9b953f09a0bdf", - "sha256:0949110db76eb1b54cecfc0c0f8468a8b9a7fd42ba23fd0d4a37d97e0b4ca203", - "sha256:0c31a39f440801cc8603547ccaacf4cb1f02b81af6ba656621c13677b27f4426", - "sha256:1e10b3fda5677d360440ebd12a1185944dc81d9ea9acf0c6b0681013b3fb9bc2", - "sha256:1f59440b993666a417ba1954cfb1b7fb11cb4dea1a1d2777897009f688d000ee", - "sha256:2b5a3806d9f656c14e9d9b693a344fc5684fdd045155594be0c505c6e9410a94", - "sha256:4a14e2d7c2c0e07b5affcfbfc5c395d767f94bb1a822934a41a3b5371cde1458", - "sha256:4cb50541225208b37786fdb0de632e475c4f00ec4792579df551ef48d6999d69", - "sha256:52999666ad01de885653e1f74a86c2a6520d1004afec475180bebf3d7393a8fc", - "sha256:562c353079e8ce7e2ad611fd7436a72f5df97be72bca59ae9ebf789a724afd5c", - "sha256:5ce2a71f473f4703daa8d6c61a00b35ce625a7f5015b4371e3af728dafca296a", - "sha256:6613e633676168a4500e5e6bb6e3e64d3fdb96d2dc472eb4b99235fb4141adb1", - "sha256:8330406f294df118399c721f80979f2516447bcc73e4262826687872c864751e", - "sha256:8e939dfa7d16609b99eb4d1fd2fc74f7a90f4fd0aaf31d611822daaff456236f", - "sha256:8fa4303e1f50d9f0c8f2f7833b5a370a94d19d41449def62b34ae072126b4dfd", - "sha256:966d987975aa3b4cfcdf1495930ff6ecb152fafe8e544e40633e41b24ca3e1c5", - "sha256:aec4ea43a1b8e9782246a259410f66692f2d3aa0f03c54477e506193b0781cb6", - "sha256:b73f889f032fbef05863f5056b46468a8262ae83628898e20b10bbbb79a3617e", - "sha256:b752088a2f819f163d11dfdbbe627b27eef9d8478c7e57d42c5e7c600fee434e", - "sha256:c8669f96277f140797e0ff99f80bd706271674942672a38ed694e2bfa66f3900", - "sha256:ccf00549efaf6f8d5b35b654beb9aed2b788a5b33b05606eb818ddaa4e924ea3", - "sha256:ce7c91463ad21ac72fc795188292b01c8366cf625e2d1e5ed473ce127b844f60", - "sha256:d776d8d47884e6ad39ff8a301f1ae6b7d2186f209218cf024f43334dbba79c64", - "sha256:dab0f63841aebb2b421fadb31f3c7eef27898f21274a8e5b45c4f2bccb40f9ed", - "sha256:daedcfbf3b24b2b687e35b33252a9315425c2dd06a085a36906d516135bdd60e", - "sha256:e7ad1ec621db2c5ad47924f63561f75abfd4fff669c62c8cc99c169c90432f59", - "sha256:f14fb6c4058772a0d74d82874d3b89d7264d89b4ed7fa0413ea0ef8112b268b9", - "sha256:f16c7b6b98bc400d180f05e65e2236ef4ee9d71f3815280558582670e1e67536", - "sha256:f2d9eb92b26600ae6e8092f66da4bcede1b61a647c9080d6b44c148aff3a8ea4", - "sha256:ffe94f9d17800610dda5282d7f6facfc216d79a93dd728a03d2f21cff3af7cc6" + "sha256:025f94fc1e1364f00e50badc88c47f98af20012f23317234e51a11333ef986e6", + "sha256:02aa7fb282606331aefbc0586e2cf540e9dbe5e343493295e7f390936ad2738e", + "sha256:057210e831573e932702cf332012ed39da78edf0f02d24a3f0b213264a87a397", + "sha256:0d946b79c56187fe139276d4c8ed612a27a616966c8b9779d6b79e2053587c8b", + "sha256:104790893b928d310aae8a955e0bdbaa442fb0ac0a33d1bbb0741c791a407778", + "sha256:15527ef218d95a8717486106553b0d54ff2641e795b65668754e17ab9ca6e381", + "sha256:1826527a0b032f6e20e7ac7f72d7c26dd476a5e5aa82c04aa1c7088a59fded7d", + "sha256:22e3aa4ce1c3eebc7f70f9ca7fd4ce1ea33e8bdb7b61996806cd312f08f84a3a", + "sha256:244e1101e9a48615b9a16cbd194f73c115fdfefc96894803158608115f703b26", + "sha256:24b8c04fdb633a84829d03909752c385faef249c06114cc8d8e1700b95aae5c8", + "sha256:2c276696350785d3104412cbe3ac70ab1e3a10c408e7b20599ee41403a3ed630", + "sha256:2d8474dc833b1182b651b184ace997a7bd83de0f51244de988d3c30e49f07de3", + "sha256:3119b57fe1d964781e91a53e81532c85ed1701baaddec592e22f6b77a9fdf3df", + "sha256:3bee8e7e0709b0fcdaa498a3e513bde9ffc7cd09dbceb11e425bd91c89dbd5b6", + "sha256:436c071e01a464753d30dbfc8768dd93aecf2a8e378e5314d130b95e77b4d612", + "sha256:46635e3f19ad04d5a7d7cf23d232388ddbfccf46d9a3b7436b6abadda4e84813", + "sha256:4772e0b679717e7ac4608d996f57b6f380748a919b457cb05bb941467b888b22", + "sha256:4e2cd80e16f481a62c3175b607373200e714ed29025f21559ebf7524f295689f", + "sha256:52732960efa0e003ca1c092dc0a3c65276e897681287a788a01ca78dda3b41f0", + "sha256:55a7de51ec7d1731b2431886d0349146645f2816e5b8eb982d7c49f89472c9f3", + "sha256:5f8ed5934197a2d4b2087646e98de3e099a237099dcf498b9e38dd3465f74ef4", + "sha256:64b064124fcbc8eb04a155117dc4d9a336e3cda3f069958fbc44fe70c3c3d1e9", + "sha256:65958b8e4319f992e85dad59d8081888b97fcdbde5f0d14bc28f2848b92d3ef1", + "sha256:7683428862e20c6a790c19e64f8ccf487f613fbc83d47e3d532df9c81668d451", + "sha256:78566d5570c75a127c2491e343dc006798a384f06be588fe9b0cbe5595711559", + "sha256:7d1cb00c093dbf1d0b16ccf123e79dee3b82608e4a2a88947695f0460eef13ff", + "sha256:8c74e2a9b594f7962c62cef7680a4cb92a96b4e6e3c2f970790da67cc0213a7e", + "sha256:8e60aa7699170f55f4b0f56ee6f8415229777ac7e4b4b1aa41fc61eec08c1f1d", + "sha256:9447b561529576d89d3bf973e5241a88cf76e45bd101963f5236888713dea774", + "sha256:970055bfeb0be373f2f5299a3db8432444bad3bc2f198753ee6c2a3a781e0959", + "sha256:a6344b8542e584e140dc3c651d68bde51270e79490aa9320f9e708f9b2c39bd5", + "sha256:ce309ca470d747b02ba6069d286a17b7df8e9c94d10d727d9cf3a64e51d85184", + "sha256:cfbd86ed4c2b2ac71bbdbcea6669bf295def7152e3722ddd9dda94ac7981f33d", + "sha256:d7929c513732dff093481f4a0954ed5ff16816365842136b17caa0b4992e49d3" ], - "version": "==3.7.1" + "version": "==3.7.2" }, "python-levenshtein": { "hashes": [ @@ -231,10 +239,10 @@ }, "raven": { "hashes": [ - "sha256:3fd787d19ebb49919268f06f19310e8112d619ef364f7989246fc8753d469888", - "sha256:95f44f3ea2c1b176d5450df4becdb96c15bf2632888f9ab193e9dd22300ce46a" + "sha256:3fa6de6efa2493a7c827472e984ce9b020797d0da16f1db67197bcc23c8fae54", + "sha256:44a13f87670836e153951af9a3c80405d36b43097db869a36e92809673692ce4" ], - "version": "==6.9.0" + "version": "==6.10.0" }, "raven-aiohttp": { "hashes": [ @@ -280,23 +288,23 @@ "sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff", "sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454" ], - "markers": "python_version >= '3.4'", "version": "==6.0" }, "yarl": { "hashes": [ - "sha256:2556b779125621b311844a072e0ed367e8409a18fa12cbd68eb1258d187820f9", - "sha256:4aec0769f1799a9d4496827292c02a7b1f75c0bab56ab2b60dd94ebb57cbd5ee", - "sha256:55369d95afaacf2fa6b49c84d18b51f1704a6560c432a0f9a1aeb23f7b971308", - "sha256:6c098b85442c8fe3303e708bbb775afd0f6b29f77612e8892627bcab4b939357", - "sha256:9182cd6f93412d32e009020a44d6d170d2093646464a88aeec2aef50592f8c78", - "sha256:c8cbc21bbfa1dd7d5386d48cc814fe3d35b80f60299cdde9279046f399c3b0d8", - "sha256:db6f70a4b09cde813a4807843abaaa60f3b15fb4a2a06f9ae9c311472662daa1", - "sha256:f17495e6fe3d377e3faac68121caef6f974fcb9e046bc075bcff40d8e5cc69a4", - "sha256:f85900b9cca0c67767bb61b2b9bd53208aaa7373dae633dbe25d179b4bf38aa7" + "sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9", + "sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f", + "sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb", + "sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320", + "sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842", + "sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0", + "sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829", + "sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310", + "sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4", + "sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8", + "sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1" ], - "markers": "python_version >= '3.4.1'", - "version": "==1.2.6" + "version": "==1.3.0" } }, "develop": { @@ -336,10 +344,10 @@ }, "alabaster": { "hashes": [ - "sha256:674bb3bab080f598371f4443c5008cbfeb1a5e622dd312395d2d82af2c54c456", - "sha256:b63b1f4dc77c074d386752ec4a8a7517600f6c0db8cd42980cae17ab7b3275d7" + "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", + "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02" ], - "version": "==0.7.11" + "version": "==0.7.12" }, "appdirs": { "hashes": [ @@ -350,18 +358,16 @@ }, "async-timeout": { "hashes": [ - "sha256:474d4bc64cee20603e225eb1ece15e248962958b45a3648a9f5cc29e827a610c", - "sha256:b3c0ddc416736619bd4a95ca31de8da6920c3b9a140c64dbef2b2fa7bf521287" + "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", + "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" ], - "markers": "python_version >= '3.5.3'", - "version": "==3.0.0" + "version": "==3.0.1" }, "atomicwrites": { "hashes": [ "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" ], - "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*'", "version": "==1.2.1" }, "attrs": { @@ -387,10 +393,10 @@ }, "certifi": { "hashes": [ - "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638", - "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a" + "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", + "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033" ], - "version": "==2018.8.24" + "version": "==2018.11.29" }, "chardet": { "hashes": [ @@ -408,10 +414,10 @@ }, "colorama": { "hashes": [ - "sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", - "sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1" + "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", + "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" ], - "version": "==0.3.9" + "version": "==0.4.1" }, "distro": { "hashes": [ @@ -437,6 +443,13 @@ ], "path": "." }, + "filelock": { + "hashes": [ + "sha256:b8d5ca5ca1c815e1574aee746650ea7301de63d87935b3463d26368b76e31633", + "sha256:d610c1bb404daf85976d7a82eb2ada120f04671007266b708606565dd03b5be6" + ], + "version": "==3.0.10" + }, "fuzzywuzzy": { "hashes": [ "sha256:5ac7c0b3f4658d2743aa17da53a55598144edbc5bee3c6863840636e6926f254", @@ -446,10 +459,10 @@ }, "idna": { "hashes": [ - "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", - "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" ], - "version": "==2.7" + "version": "==2.8" }, "idna-ssl": { "hashes": [ @@ -462,7 +475,6 @@ "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", "sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5" ], - "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*'", "version": "==1.1.0" }, "jinja2": { @@ -474,51 +486,78 @@ }, "markupsafe": { "hashes": [ - "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" + "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" ], - "version": "==1.0" + "version": "==1.1.0" }, "more-itertools": { "hashes": [ - "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", - "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", - "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d" + "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", + "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", + "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9" ], - "version": "==4.3.0" + "version": "==5.0.0" }, "multidict": { "hashes": [ - "sha256:05eeab69bf2b0664644c62bd92fabb045163e5b8d4376a31dfb52ce0210ced7b", - "sha256:0c85880efa7cadb18e3b5eef0aa075dc9c0a3064cbbaef2e20be264b9cf47a64", - "sha256:136f5a4a6a4adeacc4dc820b8b22f0a378fb74f326e259c54d1817639d1d40a0", - "sha256:14906ad3347c7d03e9101749b16611cf2028547716d0840838d3c5e2b3b0f2d3", - "sha256:1ade4a3b71b1bf9e90c5f3d034a87fe4949c087ef1f6cd727fdd766fe8bbd121", - "sha256:22939a00a511a59f9ecc0158b8db728afef57975ce3782b3a265a319d05b9b12", - "sha256:2b86b02d872bc5ba5b3a4530f6a7ba0b541458ab4f7c1429a12ac326231203f7", - "sha256:3c11e92c3dfc321014e22fb442bc9eb70e01af30d6ce442026b0c35723448c66", - "sha256:4ba3bd26f282b201fdbce351f1c5d17ceb224cbedb73d6e96e6ce391b354aacc", - "sha256:4c6e78d042e93751f60672989efbd6a6bc54213ed7ff695fff82784bbb9ea035", - "sha256:4d80d1901b89cc935a6cf5b9fd89df66565272722fe2e5473168927a9937e0ca", - "sha256:4fcf71d33178a00cc34a57b29f5dab1734b9ce0f1c97fb34666deefac6f92037", - "sha256:52f7670b41d4b4d97866ebc38121de8bcb9813128b7c4942b07794d08193c0ab", - "sha256:5368e2b7649a26b7253c6c9e53241248aab9da49099442f5be238fde436f18c9", - "sha256:5bb65fbb48999044938f0c0508e929b14a9b8bf4939d8263e9ea6691f7b54663", - "sha256:60672bb5577472800fcca1ac9dae232d1461db9f20f055184be8ce54b0052572", - "sha256:669e9be6d148fc0283f53e17dd140cde4dc7c87edac8319147edd5aa2a830771", - "sha256:6a0b7a804e8d1716aa2c72e73210b48be83d25ba9ec5cf52cf91122285707bb1", - "sha256:79034ea3da3cf2a815e3e52afdc1f6c1894468c98bdce5d2546fa2342585497f", - "sha256:79247feeef6abcc11137ad17922e865052f23447152059402fc320f99ff544bb", - "sha256:81671c2049e6bf42c7fd11a060f8bc58f58b7b3d6f3f951fc0b15e376a6a5a98", - "sha256:82ac4a5cb56cc9280d4ae52c2d2ebcd6e0668dd0f9ef17f0a9d7c82bd61e24fa", - "sha256:9436267dbbaa49dad18fbbb54f85386b0f5818d055e7b8e01d219661b6745279", - "sha256:94e4140bb1343115a1afd6d84ebf8fca5fb7bfb50e1c2cbd6f2fb5d3117ef102", - "sha256:a2cab366eae8a0ffe0813fd8e335cf0d6b9bb6c5227315f53bb457519b811537", - "sha256:a596019c3eafb1b0ae07db9f55a08578b43c79adb1fe1ab1fd818430ae59ee6f", - "sha256:e8848ae3cd6a784c29fae5055028bee9bffcc704d8bcad09bd46b42b44a833e2", - "sha256:e8a048bfd7d5a280f27527d11449a509ddedf08b58a09a24314828631c099306", - "sha256:f6dd28a0ac60e2426a6918f36f1b4e2620fc785a0de7654cd206ba842eee57fd" + "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f", + "sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3", + "sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef", + "sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b", + "sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73", + "sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc", + "sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3", + "sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd", + "sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351", + "sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941", + "sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d", + "sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1", + "sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b", + "sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a", + "sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3", + "sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7", + "sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0", + "sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0", + "sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014", + "sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5", + "sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036", + "sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d", + "sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a", + "sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce", + "sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1", + "sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a", + "sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9", + "sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7", + "sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b" ], - "version": "==4.4.2" + "version": "==4.5.2" }, "packaging": { "hashes": [ @@ -529,48 +568,45 @@ }, "pluggy": { "hashes": [ - "sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1", - "sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1" + "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", + "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a" ], - "markers": "python_version != '3.0.*' and python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.2.*' and python_version != '3.1.*'", - "version": "==0.7.1" + "version": "==0.8.1" }, "py": { "hashes": [ - "sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1", - "sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6" + "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", + "sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6" ], - "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*'", - "version": "==1.6.0" + "version": "==1.7.0" }, "pygments": { "hashes": [ - "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", - "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" + "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a", + "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d" ], - "version": "==2.2.0" + "version": "==2.3.1" }, "pyparsing": { "hashes": [ - "sha256:bc6c7146b91af3f567cf6daeaec360bc07d45ffec4cf5353f4d7a208ce7ca30a", - "sha256:d29593d8ebe7b57d6967b62494f8c72b03ac0262b1eed63826c6f788b3606401" + "sha256:40856e74d4987de5d01761a22d1621ae1c7f8774585acae358aa5c5936c6c90b", + "sha256:f353aab21fd474459d97b709e527b5571314ee5f067441dc9f88e33eecd96592" ], - "version": "==2.2.2" + "version": "==2.3.0" }, "pytest": { "hashes": [ - "sha256:7e258ee50338f4e46957f9e09a0f10fb1c2d05493fa901d113a8dafd0790de4e", - "sha256:9332147e9af2dcf46cd7ceb14d5acadb6564744ddff1fe8c17f0ce60ece7d9a2" + "sha256:3e65a22eb0d4f1bdbc1eacccf4a3198bf8d4049dea5112d70a0c61b00e748d02", + "sha256:5924060b374f62608a078494b909d341720a050b5224ff87e17e12377486a71d" ], - "version": "==3.8.2" + "version": "==4.1.0" }, "pytest-asyncio": { "hashes": [ - "sha256:a962e8e1b6ec28648c8fe214edab4e16bacdb37b52df26eb9d63050af309b2a9", - "sha256:fbd92c067c16111174a1286bfb253660f1e564e5146b39eeed1133315cf2c2cf" + "sha256:9fac5100fd716cbecf6ef89233e8590a4ad61d729d1732e0a96b84182df1daaf", + "sha256:d734718e25cfc32d2bf78d346e99d33724deeba774cc4afdf491530c6184b63b" ], - "markers": "python_version != '3.0.*' and python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.2.*' and python_version != '3.1.*'", - "version": "==0.9.0" + "version": "==0.10.0" }, "python-levenshtein": { "hashes": [ @@ -580,10 +616,10 @@ }, "pytz": { "hashes": [ - "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", - "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" + "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", + "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" ], - "version": "==2018.5" + "version": "==2018.9" }, "pyyaml": { "hashes": [ @@ -603,10 +639,10 @@ }, "raven": { "hashes": [ - "sha256:3fd787d19ebb49919268f06f19310e8112d619ef364f7989246fc8753d469888", - "sha256:95f44f3ea2c1b176d5450df4becdb96c15bf2632888f9ab193e9dd22300ce46a" + "sha256:3fa6de6efa2493a7c827472e984ce9b020797d0da16f1db67197bcc23c8fae54", + "sha256:44a13f87670836e153951af9a3c80405d36b43097db869a36e92809673692ce4" ], - "version": "==6.9.0" + "version": "==6.10.0" }, "raven-aiohttp": { "hashes": [ @@ -617,10 +653,10 @@ }, "requests": { "hashes": [ - "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", - "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" + "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", + "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" ], - "version": "==2.19.1" + "version": "==2.21.0" }, "schema": { "hashes": [ @@ -631,10 +667,10 @@ }, "six": { "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" ], - "version": "==1.11.0" + "version": "==1.12.0" }, "snowballstemmer": { "hashes": [ @@ -645,17 +681,17 @@ }, "sphinx": { "hashes": [ - "sha256:217a7705adcb573da5bbe1e0f5cab4fa0bd89fd9342c9159121746f593c2d5a4", - "sha256:a602513f385f1d5785ff1ca420d9c7eb1a1b63381733b2f0ea8188a391314a86" + "sha256:429e3172466df289f0f742471d7e30ba3ee11f3b5aecd9a840480d03f14bcfe5", + "sha256:c4cb17ba44acffae3d3209646b6baec1e215cad3065e852c68cc569d4df1b9f8" ], - "version": "==1.7.9" + "version": "==1.8.3" }, "sphinx-rtd-theme": { "hashes": [ - "sha256:3b49758a64f8a1ebd8a33cb6cc9093c3935a908b716edfaa5772fd86aac27ef6", - "sha256:80e01ec0eb711abacb1fa507f3eae8b805ae8fa3e8b057abfdf497e3f644c82c" + "sha256:02f02a676d6baabb758a20c7a479d58648e0f64f13e07d1b388e9bb2afe86a09", + "sha256:d0f6bc70f98961145c5b0e26a992829363a197321ba571b31b24ea91879e0c96" ], - "version": "==0.4.1" + "version": "==0.4.2" }, "sphinxcontrib-asyncio": { "hashes": [ @@ -668,39 +704,36 @@ "sha256:68ca7ff70785cbe1e7bccc71a48b5b6d965d79ca50629606c7861a21b206d9dd", "sha256:9de47f375baf1ea07cdb3436ff39d7a9c76042c10a769c52353ec46e4e8fc3b9" ], - "markers": "python_version != '3.0.*' and python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.2.*' and python_version != '3.1.*'", "version": "==1.1.0" }, "toml": { "hashes": [ - "sha256:380178cde50a6a79f9d2cf6f42a62a5174febe5eea4126fe4038785f1d888d42", - "sha256:a7901919d3e4f92ffba7ff40a9d697e35bbbc8a8049fe8da742f34c83606d957" + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" ], - "version": "==0.9.6" + "version": "==0.10.0" }, "tox": { "hashes": [ - "sha256:7f802b37fffd3b5ef2aab104943fa5dad24bf9564bb7e732e54b8d0cfec2fca0", - "sha256:cc97859bd7f38aa5b3b8ba55ffe7ee9952e7050faad1aedc0829cd3db2fb61d6" + "sha256:2a8d8a63660563e41e64e3b5b677e81ce1ffa5e2a93c2c565d3768c287445800", + "sha256:edfca7809925f49bdc110d0a2d9966bbf35a0c25637216d9586e7a5c5de17bfb" ], "index": "pypi", - "version": "==3.4.0" + "version": "==3.6.1" }, "urllib3": { "hashes": [ - "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", - "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" ], - "markers": "python_version != '3.0.*' and python_version != '3.3.*' and python_version != '3.2.*' and python_version < '4' and python_version >= '2.6' and python_version != '3.1.*'", - "version": "==1.23" + "version": "==1.24.1" }, "virtualenv": { "hashes": [ - "sha256:2ce32cd126117ce2c539f0134eb89de91a8413a29baac49cbab3eb50e2026669", - "sha256:ca07b4c0b54e14a91af9f34d0919790b016923d157afda5efdde55c96718f752" + "sha256:34b9ae3742abed2f95d3970acf4d80533261d6061b51160b197f84e5b4c98b4c", + "sha256:fa736831a7b18bd2bfeef746beb622a92509e9733d645952da136b0639cd40cd" ], - "markers": "python_version != '3.0.*' and python_version >= '2.7' and python_version != '3.2.*' and python_version != '3.1.*'", - "version": "==16.0.0" + "version": "==16.2.0" }, "websockets": { "hashes": [ @@ -726,23 +759,23 @@ "sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff", "sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454" ], - "markers": "python_version >= '3.4'", "version": "==6.0" }, "yarl": { "hashes": [ - "sha256:2556b779125621b311844a072e0ed367e8409a18fa12cbd68eb1258d187820f9", - "sha256:4aec0769f1799a9d4496827292c02a7b1f75c0bab56ab2b60dd94ebb57cbd5ee", - "sha256:55369d95afaacf2fa6b49c84d18b51f1704a6560c432a0f9a1aeb23f7b971308", - "sha256:6c098b85442c8fe3303e708bbb775afd0f6b29f77612e8892627bcab4b939357", - "sha256:9182cd6f93412d32e009020a44d6d170d2093646464a88aeec2aef50592f8c78", - "sha256:c8cbc21bbfa1dd7d5386d48cc814fe3d35b80f60299cdde9279046f399c3b0d8", - "sha256:db6f70a4b09cde813a4807843abaaa60f3b15fb4a2a06f9ae9c311472662daa1", - "sha256:f17495e6fe3d377e3faac68121caef6f974fcb9e046bc075bcff40d8e5cc69a4", - "sha256:f85900b9cca0c67767bb61b2b9bd53208aaa7373dae633dbe25d179b4bf38aa7" + "sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9", + "sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f", + "sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb", + "sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320", + "sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842", + "sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0", + "sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829", + "sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310", + "sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4", + "sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8", + "sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1" ], - "markers": "python_version >= '3.4.1'", - "version": "==1.2.6" + "version": "==1.3.0" } } } diff --git a/dependency_links.txt b/dependency_links.txt index 791ce4d0c..c05bff902 100644 --- a/dependency_links.txt +++ b/dependency_links.txt @@ -1 +1 @@ -https://github.com/Rapptz/discord.py/tarball/836ae730401ea370aa10127bb9c86854c8b516ac#egg=discord.py-1.0.0a0 +https://github.com/Rapptz/discord.py/tarball/7f4c57dd5ad20b7fa10aea485f674a4bc24b9547#egg=discord.py-1.0.0a0 diff --git a/redbot/cogs/cleanup/cleanup.py b/redbot/cogs/cleanup/cleanup.py index 67fcaefa7..d4b1acf6b 100644 --- a/redbot/cogs/cleanup/cleanup.py +++ b/redbot/cogs/cleanup/cleanup.py @@ -230,7 +230,6 @@ class Cleanup(commands.Cog): to_delete = await self.get_messages_for_deletion( channel=channel, number=None, after=after, delete_pinned=delete_pinned ) - to_delete.append(ctx.message) reason = "{}({}) deleted {} messages in channel {}.".format( author.name, author.id, len(to_delete), channel.name diff --git a/redbot/cogs/customcom/customcom.py b/redbot/cogs/customcom/customcom.py index c9307e817..764cc4a7f 100644 --- a/redbot/cogs/customcom/customcom.py +++ b/redbot/cogs/customcom/customcom.py @@ -358,7 +358,6 @@ class CustomCommands(commands.Cog): result = responses else: continue - # Replace newlines with spaces # Cut preview to 52 characters max if len(result) > 52: result = result[:49] + "..." @@ -369,7 +368,8 @@ class CustomCommands(commands.Cog): results.append((f"{ctx.clean_prefix}{command}", result)) if await ctx.embed_requested(): - content = "\n".join(map("**{0[0]}** {0[1]}".format, results)) + # We need a space before the newline incase the CC preview ends in link (GH-2295) + content = " \n".join(map("**{0[0]}** {0[1]}".format, results)) pages = list(pagify(content, page_length=1024)) embed_pages = [] for idx, page in enumerate(pages, start=1): diff --git a/redbot/cogs/mod/checks.py b/redbot/cogs/mod/checks.py deleted file mode 100644 index 8553695a8..000000000 --- a/redbot/cogs/mod/checks.py +++ /dev/null @@ -1,66 +0,0 @@ -from redbot.core import commands - - -def mod_or_voice_permissions(**perms): - async def pred(ctx: commands.Context): - author = ctx.author - guild = ctx.guild - if await ctx.bot.is_owner(author) or guild.owner == author: - # Author is bot owner or guild owner - return True - - admin_role = guild.get_role(await ctx.bot.db.guild(guild).admin_role()) - mod_role = guild.get_role(await ctx.bot.db.guild(guild).mod_role()) - - if admin_role in author.roles or mod_role in author.roles: - return True - - for vc in guild.voice_channels: - resolved = vc.permissions_for(author) - good = resolved.administrator or all( - getattr(resolved, name, None) == value for name, value in perms.items() - ) - if not good: - return False - else: - return True - - return commands.permissions_check(pred) - - -def admin_or_voice_permissions(**perms): - async def pred(ctx: commands.Context): - author = ctx.author - guild = ctx.guild - if await ctx.bot.is_owner(author) or guild.owner == author: - return True - admin_role = guild.get_role(await ctx.bot.db.guild(guild).admin_role()) - if admin_role in author.roles: - return True - for vc in guild.voice_channels: - resolved = vc.permissions_for(author) - good = resolved.administrator or all( - getattr(resolved, name, None) == value for name, value in perms.items() - ) - if not good: - return False - else: - return True - - return commands.permissions_check(pred) - - -def bot_has_voice_permissions(**perms): - async def pred(ctx: commands.Context): - guild = ctx.guild - for vc in guild.voice_channels: - resolved = vc.permissions_for(guild.me) - good = resolved.administrator or all( - getattr(resolved, name, None) == value for name, value in perms.items() - ) - if not good: - return False - else: - return True - - return commands.check(pred) diff --git a/redbot/cogs/mod/mod.py b/redbot/cogs/mod/mod.py index f59e75d5e..ff7a68de9 100644 --- a/redbot/cogs/mod/mod.py +++ b/redbot/cogs/mod/mod.py @@ -11,12 +11,11 @@ from discord.ext.commands.errors import BadArgument from redbot.core import checks, Config, modlog, commands from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n -from redbot.core.utils.chat_formatting import box, escape, pagify -from .checks import mod_or_voice_permissions, admin_or_voice_permissions, bot_has_voice_permissions +from redbot.core.utils.chat_formatting import box, escape, pagify, format_perms_list +from redbot.core.utils.common_filters import filter_invites, filter_various_mentions from redbot.core.utils.mod import is_mod_or_superior, is_allowed_by_hierarchy, get_audit_reason from .log import log -from redbot.core.utils.common_filters import filter_invites, filter_various_mentions _ = T_ = Translator("Mod", __file__) @@ -794,15 +793,60 @@ class Mod(commands.Cog): except discord.HTTPException: return + @staticmethod + async def _voice_perm_check( + ctx: commands.Context, user_voice_state: Optional[discord.VoiceState], **perms: bool + ) -> bool: + """Check if the bot and user have sufficient permissions for voicebans. + + This also verifies that the user's voice state and connected + channel are not ``None``. + + Returns + ------- + bool + ``True`` if the permissions are sufficient and the user has + a valid voice state. + + """ + if user_voice_state is None or user_voice_state.channel is None: + await ctx.send(_("That user is not in a voice channel.")) + return False + voice_channel: discord.VoiceChannel = user_voice_state.channel + required_perms = discord.Permissions() + required_perms.update(**perms) + if not voice_channel.permissions_for(ctx.me) >= required_perms: + await ctx.send( + _("I require the {perms} permission(s) in that user's channel to do that.").format( + perms=format_perms_list(required_perms) + ) + ) + return False + if ( + ctx.permission_state is commands.PermState.NORMAL + and not voice_channel.permissions_for(ctx.author) >= required_perms + ): + await ctx.send( + _( + "You must have the {perms} permission(s) in that user's channel to use this " + "command." + ).format(perms=format_perms_list(required_perms)) + ) + return False + return True + @commands.command() @commands.guild_only() - @admin_or_voice_permissions(mute_members=True, deafen_members=True) - @bot_has_voice_permissions(mute_members=True, deafen_members=True) + @checks.admin_or_permissions(mute_members=True, deafen_members=True) async def voiceban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): """Ban a user from speaking and listening in the server's voice channels.""" - user_voice_state = user.voice - if user_voice_state is None: - await ctx.send(_("No voice state for that user!")) + user_voice_state: discord.VoiceState = user.voice + if ( + await self._voice_perm_check( + ctx, user_voice_state, deafen_members=True, mute_members=True + ) + is False + ): return needs_mute = True if user_voice_state.mute is False else False needs_deafen = True if user_voice_state.deaf is False else False @@ -837,13 +881,15 @@ class Mod(commands.Cog): @commands.command() @commands.guild_only() - @admin_or_voice_permissions(mute_members=True, deafen_members=True) - @bot_has_voice_permissions(mute_members=True, deafen_members=True) async def voiceunban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): """Unban a user from speaking and listening in the server's voice channels.""" user_voice_state = user.voice - if user_voice_state is None: - await ctx.send(_("No voice state for that user!")) + if ( + await self._voice_perm_check( + ctx, user_voice_state, deafen_members=True, mute_members=True + ) + is False + ): return needs_unmute = True if user_voice_state.mute else False needs_undeafen = True if user_voice_state.deaf else False @@ -925,47 +971,43 @@ class Mod(commands.Cog): @mute.command(name="voice") @commands.guild_only() - @mod_or_voice_permissions(mute_members=True) - @bot_has_voice_permissions(mute_members=True) async def voice_mute(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): """Mute a user in their current voice channel.""" user_voice_state = user.voice + if ( + await self._voice_perm_check( + ctx, user_voice_state, mute_members=True, manage_channels=True + ) + is False + ): + return guild = ctx.guild author = ctx.author - if user_voice_state: - channel = user_voice_state.channel - if channel: - audit_reason = get_audit_reason(author, reason) + channel = user_voice_state.channel + audit_reason = get_audit_reason(author, reason) - success, issue = await self.mute_user(guild, channel, author, user, audit_reason) + success, issue = await self.mute_user(guild, channel, author, user, audit_reason) - if success: - await ctx.send( - _("Muted {user} in channel {channel.name}").format( - user=user, channel=channel - ) - ) - try: - await modlog.create_case( - self.bot, - guild, - ctx.message.created_at, - "vmute", - user, - author, - reason, - until=None, - channel=channel, - ) - except RuntimeError as e: - await ctx.send(e) - else: - await channel.send(issue) - else: - await ctx.send(_("That user is not in a voice channel right now!")) + if success: + await ctx.send( + _("Muted {user} in channel {channel.name}").format(user=user, channel=channel) + ) + try: + await modlog.create_case( + self.bot, + guild, + ctx.message.created_at, + "vmute", + user, + author, + reason, + until=None, + channel=channel, + ) + except RuntimeError as e: + await ctx.send(e) else: - await ctx.send(_("No voice state for the target!")) - return + await ctx.send(issue) @mute.command(name="channel") @commands.guild_only() @@ -1081,51 +1123,45 @@ class Mod(commands.Cog): @unmute.command(name="voice") @commands.guild_only() - @mod_or_voice_permissions(mute_members=True) - @bot_has_voice_permissions(mute_members=True) async def unmute_voice( self, ctx: commands.Context, user: discord.Member, *, reason: str = None ): """Unmute a user in their current voice channel.""" user_voice_state = user.voice + if ( + await self._voice_perm_check( + ctx, user_voice_state, mute_members=True, manage_channels=True + ) + is False + ): + return guild = ctx.guild author = ctx.author - if user_voice_state: - channel = user_voice_state.channel - if channel: - audit_reason = get_audit_reason(author, reason) + channel = user_voice_state.channel + audit_reason = get_audit_reason(author, reason) - success, message = await self.unmute_user( - guild, channel, author, user, audit_reason + success, message = await self.unmute_user(guild, channel, author, user, audit_reason) + + if success: + await ctx.send( + _("Unmuted {user} in channel {channel.name}").format(user=user, channel=channel) + ) + try: + await modlog.create_case( + self.bot, + guild, + ctx.message.created_at, + "vunmute", + user, + author, + reason, + until=None, + channel=channel, ) - - if success: - await ctx.send( - _("Unmuted {user} in channel {channel.name}").format( - user=user, channel=channel - ) - ) - try: - await modlog.create_case( - self.bot, - guild, - ctx.message.created_at, - "vunmute", - user, - author, - reason, - until=None, - channel=channel, - ) - except RuntimeError as e: - await ctx.send(e) - else: - await ctx.send(_("Unmute failed. Reason: {}").format(message)) - else: - await ctx.send(_("That user is not in a voice channel right now!")) + except RuntimeError as e: + await ctx.send(e) else: - await ctx.send(_("No voice state for the target!")) - return + await ctx.send(_("Unmute failed. Reason: {}").format(message)) @checks.mod_or_permissions(administrator=True) @unmute.command(name="channel") @@ -1347,8 +1383,8 @@ class Mod(commands.Cog): user = author # A special case for a special someone :^) - special_date = datetime(2016, 1, 10, 6, 8, 4, 443_000) - is_special = user.id == 96_130_341_705_637_888 and guild.id == 133_049_272_517_001_216 + special_date = datetime(2016, 1, 10, 6, 8, 4, 443000) + is_special = user.id == 96130341705637888 and guild.id == 133049272517001216 roles = sorted(user.roles)[1:] names, nicks = await self.get_names_and_nicks(user) diff --git a/redbot/core/__init__.py b/redbot/core/__init__.py index d3b2c08b3..049f4e4a5 100644 --- a/redbot/core/__init__.py +++ b/redbot/core/__init__.py @@ -148,5 +148,5 @@ class VersionInfo: ) -__version__ = "3.0.0rc2" +__version__ = "3.0.0rc3.post1" version_info = VersionInfo.from_str(__version__) diff --git a/redbot/core/bot.py b/redbot/core/bot.py index a26fd90f2..e0778f53c 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -111,7 +111,7 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): self.main_dir = bot_dir - self.cog_mgr = CogManager(paths=(str(self.main_dir / "cogs"),)) + self.cog_mgr = CogManager() super().__init__(*args, formatter=Help(), **kwargs) diff --git a/redbot/core/cog_manager.py b/redbot/core/cog_manager.py index f63cc42ed..08ea72a24 100644 --- a/redbot/core/cog_manager.py +++ b/redbot/core/cog_manager.py @@ -3,7 +3,7 @@ import pkgutil from importlib import import_module, invalidate_caches from importlib.machinery import ModuleSpec from pathlib import Path -from typing import Tuple, Union, List, Optional +from typing import Union, List, Optional import redbot.cogs from redbot.core.utils import deduplicate_iterables @@ -25,8 +25,6 @@ class NoSuchCog(ImportError): Different from ImportError because some ImportErrors can happen inside cogs. """ - pass - class CogManager: """Directory manager for Red's cogs. @@ -39,30 +37,27 @@ class CogManager: CORE_PATH = Path(redbot.cogs.__path__[0]) - def __init__(self, paths: Tuple[str] = ()): + def __init__(self): self.conf = Config.get_conf(self, 2938473984732, True) tmp_cog_install_path = cog_data_path(self) / "cogs" tmp_cog_install_path.mkdir(parents=True, exist_ok=True) self.conf.register_global(paths=[], install_path=str(tmp_cog_install_path)) - self._paths = [Path(p) for p in paths] - async def paths(self) -> Tuple[Path, ...]: - """Get all currently valid path directories. + async def paths(self) -> List[Path]: + """Get all currently valid path directories, in order of priority Returns ------- - `tuple` of `pathlib.Path` - All valid cog paths. + List[pathlib.Path] + A list of paths where cog packages can be found. The + install path is highest priority, followed by the + user-defined paths, and the core path has the lowest + priority. """ - conf_paths = [Path(p) for p in await self.conf.paths()] - other_paths = self._paths - - all_paths = deduplicate_iterables(conf_paths, other_paths, [self.CORE_PATH]) - - if self.install_path not in all_paths: - all_paths.insert(0, await self.install_path()) - return tuple(p.resolve() for p in all_paths if p.is_dir()) + return deduplicate_iterables( + [await self.install_path()], await self.user_defined_paths(), [self.CORE_PATH] + ) async def install_path(self) -> Path: """Get the install path for 3rd party cogs. @@ -73,8 +68,20 @@ class CogManager: The path to the directory where 3rd party cogs are stored. """ - p = Path(await self.conf.install_path()) - return p.resolve() + return Path(await self.conf.install_path()).resolve() + + async def user_defined_paths(self) -> List[Path]: + """Get a list of user-defined cog paths. + + All paths will be absolute and unique, in order of priority. + + Returns + ------- + List[pathlib.Path] + A list of user-defined paths. + + """ + return list(map(Path, deduplicate_iterables(await self.conf.paths()))) async def set_install_path(self, path: Path) -> Path: """Set the install path for 3rd party cogs. @@ -125,11 +132,10 @@ class CogManager: path = Path(path) return path - async def add_path(self, path: Union[Path, str]): + async def add_path(self, path: Union[Path, str]) -> None: """Add a cog path to current list. - This will ignore duplicates. Does have a side effect of removing all - invalid paths from the saved path list. + This will ignore duplicates. Parameters ---------- @@ -156,11 +162,12 @@ class CogManager: if path == self.CORE_PATH: raise ValueError("Cannot add the core path as an additional path.") - async with self.conf.paths() as paths: - if not any(Path(p) == path for p in paths): - paths.append(str(path)) + current_paths = await self.user_defined_paths() + if path not in current_paths: + current_paths.append(path) + await self.set_paths(current_paths) - async def remove_path(self, path: Union[Path, str]) -> Tuple[Path, ...]: + async def remove_path(self, path: Union[Path, str]) -> None: """Remove a path from the current paths list. Parameters @@ -168,20 +175,12 @@ class CogManager: path : `pathlib.Path` or `str` Path to remove. - Returns - ------- - `tuple` of `pathlib.Path` - Tuple of new valid paths. - """ path = self._ensure_path_obj(path).resolve() + paths = await self.user_defined_paths() - paths = [Path(p) for p in await self.conf.paths()] - if path in paths: - paths.remove(path) - await self.set_paths(paths) - - return tuple(paths) + paths.remove(path) + await self.set_paths(paths) async def set_paths(self, paths_: List[Path]): """Set the current paths list. @@ -192,7 +191,7 @@ class CogManager: List of paths to set. """ - str_paths = [str(p) for p in paths_] + str_paths = list(map(str, paths_)) await self.conf.paths.set(str_paths) async def _find_ext_cog(self, name: str) -> ModuleSpec: @@ -213,9 +212,9 @@ class CogManager: ------ NoSuchCog When no cog with the requested name was found. + """ - resolved_paths = await self.paths() - real_paths = [str(p) for p in resolved_paths if p != self.CORE_PATH] + real_paths = list(map(str, [await self.install_path()] + await self.user_defined_paths())) for finder, module_name, _ in pkgutil.iter_modules(real_paths): if name == module_name: @@ -287,10 +286,8 @@ class CogManager: return await self._find_core_cog(name) async def available_modules(self) -> List[str]: - """Finds the names of all available modules to load. - """ - paths = (await self.install_path(),) + await self.paths() - paths = [str(p) for p in paths] + """Finds the names of all available modules to load.""" + paths = list(map(str, await self.paths())) ret = [] for finder, module_name, _ in pkgutil.iter_modules(paths): @@ -314,13 +311,6 @@ _ = Translator("CogManagerUI", __file__) class CogManagerUI(commands.Cog): """Commands to interface with Red's cog manager.""" - @staticmethod - async def visible_paths(ctx): - install_path = await ctx.bot.cog_mgr.install_path() - cog_paths = await ctx.bot.cog_mgr.paths() - cog_paths = [p for p in cog_paths if p != install_path] - return cog_paths - @commands.command() @checks.is_owner() async def paths(self, ctx: commands.Context): @@ -330,8 +320,7 @@ class CogManagerUI(commands.Cog): cog_mgr = ctx.bot.cog_mgr install_path = await cog_mgr.install_path() core_path = cog_mgr.CORE_PATH - cog_paths = await cog_mgr.paths() - cog_paths = [p for p in cog_paths if p not in (install_path, core_path)] + cog_paths = await cog_mgr.user_defined_paths() msg = _("Install Path: {install_path}\nCore Path: {core_path}\n\n").format( install_path=install_path, core_path=core_path @@ -369,7 +358,11 @@ class CogManagerUI(commands.Cog): from !paths """ path_number -= 1 - cog_paths = await self.visible_paths(ctx) + if path_number < 0: + await ctx.send(_("Path numbers must be positive.")) + return + + cog_paths = await ctx.bot.cog_mgr.user_defined_paths() try: to_remove = cog_paths.pop(path_number) except IndexError: @@ -388,8 +381,11 @@ class CogManagerUI(commands.Cog): # Doing this because in the paths command they're 1 indexed from_ -= 1 to -= 1 + if from_ < 0 or to < 0: + await ctx.send(_("Path numbers must be positive.")) + return - all_paths = await self.visible_paths(ctx) + all_paths = await ctx.bot.cog_mgr.user_defined_paths() try: to_move = all_paths.pop(from_) except IndexError: diff --git a/redbot/core/commands/commands.py b/redbot/core/commands/commands.py index 0c0a77323..ea37af87a 100644 --- a/redbot/core/commands/commands.py +++ b/redbot/core/commands/commands.py @@ -145,7 +145,7 @@ class Command(CogCommandMixin, commands.Command): @property def parents(self) -> List["Group"]: - """List[Group] : Returns all parent commands of this command. + """List[commands.Group] : Returns all parent commands of this command. This is sorted by the length of :attr:`.qualified_name` from highest to lowest. If the command has no parents, this will be an empty list. diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index 8341116f7..89f7a3918 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -1173,6 +1173,9 @@ class Core(commands.Cog, CoreLogic): await ctx.send( _("A backup has been made of this instance. It is at {}.").format(backup_file) ) + if backup_file.stat().st_size > 8_000_000: + await ctx.send(_("This backup is to large to send via DM.")) + return await ctx.send(_("Would you like to receive a copy via DM? (y/n)")) pred = MessagePredicate.yes_or_no(ctx) @@ -1183,10 +1186,18 @@ class Core(commands.Cog, CoreLogic): else: if pred.result is True: await ctx.send(_("OK, it's on its way!")) - async with ctx.author.typing(): - await ctx.author.send( - _("Here's a copy of the backup"), file=discord.File(str(backup_file)) + try: + async with ctx.author.typing(): + await ctx.author.send( + _("Here's a copy of the backup"), + file=discord.File(str(backup_file)), + ) + except discord.Forbidden: + await ctx.send( + _("I don't seem to be able to DM you. Do you have closed DMs?") ) + except discord.HTTPException: + await ctx.send(_("I could not send the backup file.")) else: await ctx.send(_("OK then.")) else: diff --git a/redbot/core/events.py b/redbot/core/events.py index 427eea041..9286fb2d5 100644 --- a/redbot/core/events.py +++ b/redbot/core/events.py @@ -15,7 +15,7 @@ from pkg_resources import DistributionNotFound from . import __version__ as red_version, version_info as red_version_info, VersionInfo, commands from .data_manager import storage_type -from .utils.chat_formatting import inline, bordered, humanize_list +from .utils.chat_formatting import inline, bordered, format_perms_list from .utils import fuzzy_command_search, format_fuzzy_results log = logging.getLogger("red") @@ -234,18 +234,13 @@ def init_events(bot, cli_flags): else: await ctx.send(await format_fuzzy_results(ctx, fuzzy_commands, embed=False)) elif isinstance(error, commands.BotMissingPermissions): - missing_perms: List[str] = [] - for perm, value in error.missing: - if value is True: - perm_name = '"' + perm.replace("_", " ").title() + '"' - missing_perms.append(perm_name) - if len(missing_perms) == 1: + if bin(error.missing.value).count("1") == 1: # Only one perm missing plural = "" else: plural = "s" await ctx.send( "I require the {perms} permission{plural} to execute that command.".format( - perms=humanize_list(missing_perms), plural=plural + perms=format_perms_list(error.missing), plural=plural ) ) elif isinstance(error, commands.CheckFailure): diff --git a/redbot/core/utils/chat_formatting.py b/redbot/core/utils/chat_formatting.py index 4fea93fe8..f0fb8aa21 100644 --- a/redbot/core/utils/chat_formatting.py +++ b/redbot/core/utils/chat_formatting.py @@ -1,5 +1,8 @@ import itertools from typing import Sequence, Iterator, List + +import discord + from redbot.core.i18n import Translator _ = Translator("UtilsChatFormatting", __file__) @@ -329,7 +332,7 @@ def escape(text: str, *, mass_mentions: bool = False, formatting: bool = False) return text -def humanize_list(items: Sequence[str]): +def humanize_list(items: Sequence[str]) -> str: """Get comma-separted list, with the last element joined with *and*. This uses an Oxford comma, because without one, items containing @@ -357,3 +360,29 @@ def humanize_list(items: Sequence[str]): if len(items) == 1: return items[0] return ", ".join(items[:-1]) + _(", and ") + items[-1] + + +def format_perms_list(perms: discord.Permissions) -> str: + """Format a list of permission names. + + This will return a humanized list of the names of all enabled + permissions in the provided `discord.Permissions` object. + + Parameters + ---------- + perms : discord.Permissions + The permissions object with the requested permissions to list + enabled. + + Returns + ------- + str + The humanized list. + + """ + perm_names: List[str] = [] + for perm, value in perms: + if value is True: + perm_name = '"' + perm.replace("_", " ").title() + '"' + perm_names.append(perm_name) + return humanize_list(perm_names).replace("Guild", "Server") diff --git a/redbot/core/utils/menus.py b/redbot/core/utils/menus.py index 3bc72f677..b60929e09 100644 --- a/redbot/core/utils/menus.py +++ b/redbot/core/utils/menus.py @@ -73,10 +73,13 @@ async def menu( # noinspection PyAsyncCall start_adding_reactions(message, controls.keys(), ctx.bot.loop) else: - if isinstance(current_page, discord.Embed): - await message.edit(embed=current_page) - else: - await message.edit(content=current_page) + try: + if isinstance(current_page, discord.Embed): + await message.edit(embed=current_page) + else: + await message.edit(content=current_page) + except discord.NotFound: + return try: react, user = await ctx.bot.wait_for( @@ -90,9 +93,12 @@ async def menu( except discord.Forbidden: # cannot remove all reactions for key in controls.keys(): await message.remove_reaction(key, ctx.bot.user) - return None - - return await controls[react.emoji](ctx, pages, controls, message, page, timeout, react.emoji) + except discord.NotFound: + return + else: + return await controls[react.emoji]( + ctx, pages, controls, message, page, timeout, react.emoji + ) async def next_page( @@ -106,10 +112,8 @@ async def next_page( ): perms = message.channel.permissions_for(ctx.me) if perms.manage_messages: # Can manage messages, so remove react - try: + with contextlib.suppress(discord.NotFound): await message.remove_reaction(emoji, ctx.author) - except discord.NotFound: - pass if page == len(pages) - 1: page = 0 # Loop around to the first item else: @@ -128,10 +132,8 @@ async def prev_page( ): perms = message.channel.permissions_for(ctx.me) if perms.manage_messages: # Can manage messages, so remove react - try: + with contextlib.suppress(discord.NotFound): await message.remove_reaction(emoji, ctx.author) - except discord.NotFound: - pass if page == 0: page = len(pages) - 1 # Loop around to the last item else: @@ -148,9 +150,8 @@ async def close_menu( timeout: float, emoji: str, ): - if message: + with contextlib.suppress(discord.NotFound): await message.delete() - return None def start_adding_reactions( @@ -161,7 +162,7 @@ def start_adding_reactions( """Start adding reactions to a message. This is a non-blocking operation - calling this will schedule the - reactions being added, but will the calling code will continue to + reactions being added, but the calling code will continue to execute asynchronously. There is no need to await this function. This is particularly useful if you wish to start waiting for a @@ -169,7 +170,7 @@ def start_adding_reactions( this is exactly what `menu` uses to do that. This spawns a `asyncio.Task` object and schedules it on ``loop``. - If ``loop`` omitted, the loop will be retreived with + If ``loop`` omitted, the loop will be retrieved with `asyncio.get_event_loop`. Parameters diff --git a/redbot/core/utils/tunnel.py b/redbot/core/utils/tunnel.py index 5b757543d..aedcd7474 100644 --- a/redbot/core/utils/tunnel.py +++ b/redbot/core/utils/tunnel.py @@ -2,7 +2,6 @@ import discord from datetime import datetime from redbot.core.utils.chat_formatting import pagify import io -import sys import weakref from typing import List, Optional from .common_filters import filter_mass_mentions @@ -151,15 +150,12 @@ class Tunnel(metaclass=TunnelMeta): """ files = [] - size = 0 - max_size = 8 * 1024 * 1024 - for a in m.attachments: - _fp = io.BytesIO() - await a.save(_fp) - size += sys.getsizeof(_fp) - if size > max_size: - return [] - files.append(discord.File(_fp, filename=a.filename)) + max_size = 8 * 1000 * 1000 + if m.attachments and sum(a.size for a in m.attachments) <= max_size: + for a in m.attachments: + _fp = io.BytesIO() + await a.save(_fp) + files.append(discord.File(_fp, filename=a.filename)) return files async def communicate( diff --git a/redbot/setup.py b/redbot/setup.py index 10cca6362..3f93c6da4 100644 --- a/redbot/setup.py +++ b/redbot/setup.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python - +#!/usr/bin/env python3 import argparse import asyncio import json @@ -177,26 +176,21 @@ def basic_setup(): async def json_to_mongo(current_data_dir: Path, storage_details: dict): from redbot.core.drivers.red_mongo import Mongo - core_data_file = list(current_data_dir.glob("core/settings.json"))[0] - m = Mongo("Core", "0", **storage_details) + core_data_file = current_data_dir / "core" / "settings.json" + driver = Mongo(cog_name="Core", identifier="0", **storage_details) with core_data_file.open(mode="r") as f: core_data = json.loads(f.read()) - collection = m.get_collection() - await collection.update_one( - {"_id": m.unique_cog_identifier}, update={"$set": core_data["0"]}, upsert=True - ) + data = core_data.get("0", {}) + for key, value in data.items(): + await driver.set(key, value=value) for p in current_data_dir.glob("cogs/**/settings.json"): + cog_name = p.parent.stem with p.open(mode="r") as f: - cog_data = json.loads(f.read()) - cog_i = None - for ident in list(cog_data.keys()): - cog_i = str(hash(ident)) - cog_m = Mongo(p.parent.stem, cog_i, **storage_details) - cog_c = cog_m.get_collection() - for ident in list(cog_data.keys()): - await cog_c.update_one( - {"_id": cog_m.unique_cog_identifier}, update={"$set": cog_data[cog_i]}, upsert=True - ) + cog_data = json.load(f) + for identifier, data in cog_data.items(): + driver = Mongo(cog_name, identifier, **storage_details) + for key, value in data.items(): + await driver.set(key, value=value) async def mongo_to_json(current_data_dir: Path, storage_details: dict): diff --git a/setup.py b/setup.py index 22b92a58b..0c463f01a 100644 --- a/setup.py +++ b/setup.py @@ -9,59 +9,59 @@ install_requires = [ "aiohttp-json-rpc==0.11.2", "aiohttp==3.4.4", "appdirs==1.4.3", - "async-timeout==3.0.0", + "async-timeout==3.0.1", "attrs==18.2.0", "chardet==3.0.4", - "colorama==0.3.9", + "colorama==0.4.1", "discord.py>=1.0.0a0", "distro==1.3.0; sys_platform == 'linux'", "fuzzywuzzy==0.17.0", "idna-ssl==1.1.0", - "idna==2.7", - "multidict==4.4.2", + "idna==2.8", + "multidict==4.5.2", "python-levenshtein==0.12.0", "pyyaml==3.13", - "raven==6.9.0", + "raven==6.10.0", "raven-aiohttp==0.7.0", "schema==0.6.8", "websockets==6.0", - "yarl==1.2.6", + "yarl==1.3.0", ] extras_require = { "test": [ "atomicwrites==1.2.1", - "more-itertools==4.3.0", - "pluggy==0.7.1", - "py==1.6.0", - "pytest==3.8.2", - "pytest-asyncio==0.9.0", - "six==1.11.0", + "more-itertools==5.0.0", + "pluggy==0.8.1", + "py==1.7.0", + "pytest==4.1.0", + "pytest-asyncio==0.10.0", + "six==1.12.0", ], - "mongo": ["motor==2.0.0", "pymongo==3.7.1", "dnspython==1.15.0"], + "mongo": ["motor==2.0.0", "pymongo==3.7.2", "dnspython==1.16.0"], "docs": [ - "alabaster==0.7.11", + "alabaster==0.7.12", "babel==2.6.0", - "certifi==2018.8.24", + "certifi==2018.11.29", "docutils==0.14", "imagesize==1.1.0", "Jinja2==2.10", - "MarkupSafe==1.0", + "MarkupSafe==1.1.0", "packaging==18.0", - "pyparsing==2.2.2", - "Pygments==2.2.0", - "pytz==2018.5", - "requests==2.19.1", - "urllib3==1.23", - "six==1.11.0", + "pyparsing==2.3.0", + "Pygments==2.3.1", + "pytz==2018.9", + "requests==2.21.0", + "six==1.12.0", "snowballstemmer==1.2.1", - "sphinx==1.7.9", - "sphinx_rtd_theme==0.4.1", + "sphinx==1.8.3", + "sphinx_rtd_theme==0.4.2", "sphinxcontrib-asyncio==0.2.0", "sphinxcontrib-websupport==1.1.0", + "urllib3==1.24.1", ], "voice": ["red-lavalink==0.1.2"], - "style": ["black==18.9b0", "click==7.0", "toml==0.9.6"], + "style": ["black==18.9b0", "click==7.0", "toml==0.10.0"], } python_requires = ">=3.6.2,<3.8"