Merge branch 'V3/release/3.0.0' into V3/develop

# Conflicts:
#	redbot/cogs/mod/mod.py
This commit is contained in:
Toby Harradine 2019-01-11 16:42:42 +11:00
commit e07408161a
18 changed files with 529 additions and 503 deletions

View File

@ -34,6 +34,7 @@ jobs:
python: 3.6.6 python: 3.6.6
env: env:
- DEPLOYING=true - DEPLOYING=true
- TOXENV=py36
deploy: deploy:
- provider: pypi - provider: pypi
user: Red-DiscordBot user: Red-DiscordBot
@ -49,12 +50,13 @@ jobs:
python: 3.6.6 python: 3.6.6
env: env:
- DEPLOYING=true - DEPLOYING=true
- TOXENV=py36
before_deploy: before_deploy:
- curl https://artifacts.crowdin.com/repo/GPG-KEY-crowdin | sudo apt-key add - - 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 - echo "deb https://artifacts.crowdin.com/repo/deb/ /" | sudo tee -a /etc/apt/sources.list
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install -y crowdin - sudo apt-get install -y crowdin
- pip install redgettext==2.1 - pip install redgettext==2.2
deploy: deploy:
- provider: script - provider: script
script: make gettext script: make gettext

453
Pipfile.lock generated
View File

@ -57,11 +57,10 @@
}, },
"async-timeout": { "async-timeout": {
"hashes": [ "hashes": [
"sha256:474d4bc64cee20603e225eb1ece15e248962958b45a3648a9f5cc29e827a610c", "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:b3c0ddc416736619bd4a95ca31de8da6920c3b9a140c64dbef2b2fa7bf521287" "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
], ],
"markers": "python_version >= '3.5.3'", "version": "==3.0.1"
"version": "==3.0.0"
}, },
"attrs": { "attrs": {
"hashes": [ "hashes": [
@ -79,15 +78,20 @@
}, },
"colorama": { "colorama": {
"hashes": [ "hashes": [
"sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d",
"sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1" "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": { "discord.py": {
"editable": true, "editable": true,
"git": "git://github.com/Rapptz/discord.py", "git": "git://github.com/Rapptz/discord.py",
"ref": "836ae730401ea370aa10127bb9c86854c8b516ac" "ref": "rewrite"
}, },
"distro": { "distro": {
"hashes": [ "hashes": [
@ -98,10 +102,10 @@
}, },
"dnspython": { "dnspython": {
"hashes": [ "hashes": [
"sha256:40f563e1f7a7b80dc5a4e76ad75c23da53d62f1e15e6e517293b04e1f84ead7c", "sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
"sha256:861e6e58faa730f9845aaaa9c6c832851fbf89382ac52915a51f89c71accdd31" "sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
], ],
"version": "==1.15.0" "version": "==1.16.0"
}, },
"e1839a8": { "e1839a8": {
"editable": true, "editable": true,
@ -120,10 +124,10 @@
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
], ],
"version": "==2.7" "version": "==2.8"
}, },
"idna-ssl": { "idna-ssl": {
"hashes": [ "hashes": [
@ -140,72 +144,76 @@
}, },
"multidict": { "multidict": {
"hashes": [ "hashes": [
"sha256:05eeab69bf2b0664644c62bd92fabb045163e5b8d4376a31dfb52ce0210ced7b", "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
"sha256:0c85880efa7cadb18e3b5eef0aa075dc9c0a3064cbbaef2e20be264b9cf47a64", "sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3",
"sha256:136f5a4a6a4adeacc4dc820b8b22f0a378fb74f326e259c54d1817639d1d40a0", "sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef",
"sha256:14906ad3347c7d03e9101749b16611cf2028547716d0840838d3c5e2b3b0f2d3", "sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b",
"sha256:1ade4a3b71b1bf9e90c5f3d034a87fe4949c087ef1f6cd727fdd766fe8bbd121", "sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73",
"sha256:22939a00a511a59f9ecc0158b8db728afef57975ce3782b3a265a319d05b9b12", "sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc",
"sha256:2b86b02d872bc5ba5b3a4530f6a7ba0b541458ab4f7c1429a12ac326231203f7", "sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3",
"sha256:3c11e92c3dfc321014e22fb442bc9eb70e01af30d6ce442026b0c35723448c66", "sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd",
"sha256:4ba3bd26f282b201fdbce351f1c5d17ceb224cbedb73d6e96e6ce391b354aacc", "sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351",
"sha256:4c6e78d042e93751f60672989efbd6a6bc54213ed7ff695fff82784bbb9ea035", "sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941",
"sha256:4d80d1901b89cc935a6cf5b9fd89df66565272722fe2e5473168927a9937e0ca", "sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d",
"sha256:4fcf71d33178a00cc34a57b29f5dab1734b9ce0f1c97fb34666deefac6f92037", "sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1",
"sha256:52f7670b41d4b4d97866ebc38121de8bcb9813128b7c4942b07794d08193c0ab", "sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b",
"sha256:5368e2b7649a26b7253c6c9e53241248aab9da49099442f5be238fde436f18c9", "sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a",
"sha256:5bb65fbb48999044938f0c0508e929b14a9b8bf4939d8263e9ea6691f7b54663", "sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3",
"sha256:60672bb5577472800fcca1ac9dae232d1461db9f20f055184be8ce54b0052572", "sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7",
"sha256:669e9be6d148fc0283f53e17dd140cde4dc7c87edac8319147edd5aa2a830771", "sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0",
"sha256:6a0b7a804e8d1716aa2c72e73210b48be83d25ba9ec5cf52cf91122285707bb1", "sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0",
"sha256:79034ea3da3cf2a815e3e52afdc1f6c1894468c98bdce5d2546fa2342585497f", "sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014",
"sha256:79247feeef6abcc11137ad17922e865052f23447152059402fc320f99ff544bb", "sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5",
"sha256:81671c2049e6bf42c7fd11a060f8bc58f58b7b3d6f3f951fc0b15e376a6a5a98", "sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036",
"sha256:82ac4a5cb56cc9280d4ae52c2d2ebcd6e0668dd0f9ef17f0a9d7c82bd61e24fa", "sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d",
"sha256:9436267dbbaa49dad18fbbb54f85386b0f5818d055e7b8e01d219661b6745279", "sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a",
"sha256:94e4140bb1343115a1afd6d84ebf8fca5fb7bfb50e1c2cbd6f2fb5d3117ef102", "sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce",
"sha256:a2cab366eae8a0ffe0813fd8e335cf0d6b9bb6c5227315f53bb457519b811537", "sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1",
"sha256:a596019c3eafb1b0ae07db9f55a08578b43c79adb1fe1ab1fd818430ae59ee6f", "sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a",
"sha256:e8848ae3cd6a784c29fae5055028bee9bffcc704d8bcad09bd46b42b44a833e2", "sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9",
"sha256:e8a048bfd7d5a280f27527d11449a509ddedf08b58a09a24314828631c099306", "sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7",
"sha256:f6dd28a0ac60e2426a6918f36f1b4e2620fc785a0de7654cd206ba842eee57fd" "sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b"
], ],
"version": "==4.4.2" "version": "==4.5.2"
}, },
"pymongo": { "pymongo": {
"hashes": [ "hashes": [
"sha256:08dea6dbff33363419af7af3bf2e9a373ff71eb22833dd7063f9b953f09a0bdf", "sha256:025f94fc1e1364f00e50badc88c47f98af20012f23317234e51a11333ef986e6",
"sha256:0949110db76eb1b54cecfc0c0f8468a8b9a7fd42ba23fd0d4a37d97e0b4ca203", "sha256:02aa7fb282606331aefbc0586e2cf540e9dbe5e343493295e7f390936ad2738e",
"sha256:0c31a39f440801cc8603547ccaacf4cb1f02b81af6ba656621c13677b27f4426", "sha256:057210e831573e932702cf332012ed39da78edf0f02d24a3f0b213264a87a397",
"sha256:1e10b3fda5677d360440ebd12a1185944dc81d9ea9acf0c6b0681013b3fb9bc2", "sha256:0d946b79c56187fe139276d4c8ed612a27a616966c8b9779d6b79e2053587c8b",
"sha256:1f59440b993666a417ba1954cfb1b7fb11cb4dea1a1d2777897009f688d000ee", "sha256:104790893b928d310aae8a955e0bdbaa442fb0ac0a33d1bbb0741c791a407778",
"sha256:2b5a3806d9f656c14e9d9b693a344fc5684fdd045155594be0c505c6e9410a94", "sha256:15527ef218d95a8717486106553b0d54ff2641e795b65668754e17ab9ca6e381",
"sha256:4a14e2d7c2c0e07b5affcfbfc5c395d767f94bb1a822934a41a3b5371cde1458", "sha256:1826527a0b032f6e20e7ac7f72d7c26dd476a5e5aa82c04aa1c7088a59fded7d",
"sha256:4cb50541225208b37786fdb0de632e475c4f00ec4792579df551ef48d6999d69", "sha256:22e3aa4ce1c3eebc7f70f9ca7fd4ce1ea33e8bdb7b61996806cd312f08f84a3a",
"sha256:52999666ad01de885653e1f74a86c2a6520d1004afec475180bebf3d7393a8fc", "sha256:244e1101e9a48615b9a16cbd194f73c115fdfefc96894803158608115f703b26",
"sha256:562c353079e8ce7e2ad611fd7436a72f5df97be72bca59ae9ebf789a724afd5c", "sha256:24b8c04fdb633a84829d03909752c385faef249c06114cc8d8e1700b95aae5c8",
"sha256:5ce2a71f473f4703daa8d6c61a00b35ce625a7f5015b4371e3af728dafca296a", "sha256:2c276696350785d3104412cbe3ac70ab1e3a10c408e7b20599ee41403a3ed630",
"sha256:6613e633676168a4500e5e6bb6e3e64d3fdb96d2dc472eb4b99235fb4141adb1", "sha256:2d8474dc833b1182b651b184ace997a7bd83de0f51244de988d3c30e49f07de3",
"sha256:8330406f294df118399c721f80979f2516447bcc73e4262826687872c864751e", "sha256:3119b57fe1d964781e91a53e81532c85ed1701baaddec592e22f6b77a9fdf3df",
"sha256:8e939dfa7d16609b99eb4d1fd2fc74f7a90f4fd0aaf31d611822daaff456236f", "sha256:3bee8e7e0709b0fcdaa498a3e513bde9ffc7cd09dbceb11e425bd91c89dbd5b6",
"sha256:8fa4303e1f50d9f0c8f2f7833b5a370a94d19d41449def62b34ae072126b4dfd", "sha256:436c071e01a464753d30dbfc8768dd93aecf2a8e378e5314d130b95e77b4d612",
"sha256:966d987975aa3b4cfcdf1495930ff6ecb152fafe8e544e40633e41b24ca3e1c5", "sha256:46635e3f19ad04d5a7d7cf23d232388ddbfccf46d9a3b7436b6abadda4e84813",
"sha256:aec4ea43a1b8e9782246a259410f66692f2d3aa0f03c54477e506193b0781cb6", "sha256:4772e0b679717e7ac4608d996f57b6f380748a919b457cb05bb941467b888b22",
"sha256:b73f889f032fbef05863f5056b46468a8262ae83628898e20b10bbbb79a3617e", "sha256:4e2cd80e16f481a62c3175b607373200e714ed29025f21559ebf7524f295689f",
"sha256:b752088a2f819f163d11dfdbbe627b27eef9d8478c7e57d42c5e7c600fee434e", "sha256:52732960efa0e003ca1c092dc0a3c65276e897681287a788a01ca78dda3b41f0",
"sha256:c8669f96277f140797e0ff99f80bd706271674942672a38ed694e2bfa66f3900", "sha256:55a7de51ec7d1731b2431886d0349146645f2816e5b8eb982d7c49f89472c9f3",
"sha256:ccf00549efaf6f8d5b35b654beb9aed2b788a5b33b05606eb818ddaa4e924ea3", "sha256:5f8ed5934197a2d4b2087646e98de3e099a237099dcf498b9e38dd3465f74ef4",
"sha256:ce7c91463ad21ac72fc795188292b01c8366cf625e2d1e5ed473ce127b844f60", "sha256:64b064124fcbc8eb04a155117dc4d9a336e3cda3f069958fbc44fe70c3c3d1e9",
"sha256:d776d8d47884e6ad39ff8a301f1ae6b7d2186f209218cf024f43334dbba79c64", "sha256:65958b8e4319f992e85dad59d8081888b97fcdbde5f0d14bc28f2848b92d3ef1",
"sha256:dab0f63841aebb2b421fadb31f3c7eef27898f21274a8e5b45c4f2bccb40f9ed", "sha256:7683428862e20c6a790c19e64f8ccf487f613fbc83d47e3d532df9c81668d451",
"sha256:daedcfbf3b24b2b687e35b33252a9315425c2dd06a085a36906d516135bdd60e", "sha256:78566d5570c75a127c2491e343dc006798a384f06be588fe9b0cbe5595711559",
"sha256:e7ad1ec621db2c5ad47924f63561f75abfd4fff669c62c8cc99c169c90432f59", "sha256:7d1cb00c093dbf1d0b16ccf123e79dee3b82608e4a2a88947695f0460eef13ff",
"sha256:f14fb6c4058772a0d74d82874d3b89d7264d89b4ed7fa0413ea0ef8112b268b9", "sha256:8c74e2a9b594f7962c62cef7680a4cb92a96b4e6e3c2f970790da67cc0213a7e",
"sha256:f16c7b6b98bc400d180f05e65e2236ef4ee9d71f3815280558582670e1e67536", "sha256:8e60aa7699170f55f4b0f56ee6f8415229777ac7e4b4b1aa41fc61eec08c1f1d",
"sha256:f2d9eb92b26600ae6e8092f66da4bcede1b61a647c9080d6b44c148aff3a8ea4", "sha256:9447b561529576d89d3bf973e5241a88cf76e45bd101963f5236888713dea774",
"sha256:ffe94f9d17800610dda5282d7f6facfc216d79a93dd728a03d2f21cff3af7cc6" "sha256:970055bfeb0be373f2f5299a3db8432444bad3bc2f198753ee6c2a3a781e0959",
"sha256:a6344b8542e584e140dc3c651d68bde51270e79490aa9320f9e708f9b2c39bd5",
"sha256:ce309ca470d747b02ba6069d286a17b7df8e9c94d10d727d9cf3a64e51d85184",
"sha256:cfbd86ed4c2b2ac71bbdbcea6669bf295def7152e3722ddd9dda94ac7981f33d",
"sha256:d7929c513732dff093481f4a0954ed5ff16816365842136b17caa0b4992e49d3"
], ],
"version": "==3.7.1" "version": "==3.7.2"
}, },
"python-levenshtein": { "python-levenshtein": {
"hashes": [ "hashes": [
@ -231,10 +239,10 @@
}, },
"raven": { "raven": {
"hashes": [ "hashes": [
"sha256:3fd787d19ebb49919268f06f19310e8112d619ef364f7989246fc8753d469888", "sha256:3fa6de6efa2493a7c827472e984ce9b020797d0da16f1db67197bcc23c8fae54",
"sha256:95f44f3ea2c1b176d5450df4becdb96c15bf2632888f9ab193e9dd22300ce46a" "sha256:44a13f87670836e153951af9a3c80405d36b43097db869a36e92809673692ce4"
], ],
"version": "==6.9.0" "version": "==6.10.0"
}, },
"raven-aiohttp": { "raven-aiohttp": {
"hashes": [ "hashes": [
@ -280,23 +288,23 @@
"sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff", "sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff",
"sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454" "sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454"
], ],
"markers": "python_version >= '3.4'",
"version": "==6.0" "version": "==6.0"
}, },
"yarl": { "yarl": {
"hashes": [ "hashes": [
"sha256:2556b779125621b311844a072e0ed367e8409a18fa12cbd68eb1258d187820f9", "sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9",
"sha256:4aec0769f1799a9d4496827292c02a7b1f75c0bab56ab2b60dd94ebb57cbd5ee", "sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f",
"sha256:55369d95afaacf2fa6b49c84d18b51f1704a6560c432a0f9a1aeb23f7b971308", "sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb",
"sha256:6c098b85442c8fe3303e708bbb775afd0f6b29f77612e8892627bcab4b939357", "sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320",
"sha256:9182cd6f93412d32e009020a44d6d170d2093646464a88aeec2aef50592f8c78", "sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842",
"sha256:c8cbc21bbfa1dd7d5386d48cc814fe3d35b80f60299cdde9279046f399c3b0d8", "sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0",
"sha256:db6f70a4b09cde813a4807843abaaa60f3b15fb4a2a06f9ae9c311472662daa1", "sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829",
"sha256:f17495e6fe3d377e3faac68121caef6f974fcb9e046bc075bcff40d8e5cc69a4", "sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310",
"sha256:f85900b9cca0c67767bb61b2b9bd53208aaa7373dae633dbe25d179b4bf38aa7" "sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4",
"sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8",
"sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1"
], ],
"markers": "python_version >= '3.4.1'", "version": "==1.3.0"
"version": "==1.2.6"
} }
}, },
"develop": { "develop": {
@ -336,10 +344,10 @@
}, },
"alabaster": { "alabaster": {
"hashes": [ "hashes": [
"sha256:674bb3bab080f598371f4443c5008cbfeb1a5e622dd312395d2d82af2c54c456", "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
"sha256:b63b1f4dc77c074d386752ec4a8a7517600f6c0db8cd42980cae17ab7b3275d7" "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
], ],
"version": "==0.7.11" "version": "==0.7.12"
}, },
"appdirs": { "appdirs": {
"hashes": [ "hashes": [
@ -350,18 +358,16 @@
}, },
"async-timeout": { "async-timeout": {
"hashes": [ "hashes": [
"sha256:474d4bc64cee20603e225eb1ece15e248962958b45a3648a9f5cc29e827a610c", "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:b3c0ddc416736619bd4a95ca31de8da6920c3b9a140c64dbef2b2fa7bf521287" "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
], ],
"markers": "python_version >= '3.5.3'", "version": "==3.0.1"
"version": "==3.0.0"
}, },
"atomicwrites": { "atomicwrites": {
"hashes": [ "hashes": [
"sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
"sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" "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" "version": "==1.2.1"
}, },
"attrs": { "attrs": {
@ -387,10 +393,10 @@
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638", "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
"sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a" "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
], ],
"version": "==2018.8.24" "version": "==2018.11.29"
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
@ -408,10 +414,10 @@
}, },
"colorama": { "colorama": {
"hashes": [ "hashes": [
"sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d",
"sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1" "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"
], ],
"version": "==0.3.9" "version": "==0.4.1"
}, },
"distro": { "distro": {
"hashes": [ "hashes": [
@ -437,6 +443,13 @@
], ],
"path": "." "path": "."
}, },
"filelock": {
"hashes": [
"sha256:b8d5ca5ca1c815e1574aee746650ea7301de63d87935b3463d26368b76e31633",
"sha256:d610c1bb404daf85976d7a82eb2ada120f04671007266b708606565dd03b5be6"
],
"version": "==3.0.10"
},
"fuzzywuzzy": { "fuzzywuzzy": {
"hashes": [ "hashes": [
"sha256:5ac7c0b3f4658d2743aa17da53a55598144edbc5bee3c6863840636e6926f254", "sha256:5ac7c0b3f4658d2743aa17da53a55598144edbc5bee3c6863840636e6926f254",
@ -446,10 +459,10 @@
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
], ],
"version": "==2.7" "version": "==2.8"
}, },
"idna-ssl": { "idna-ssl": {
"hashes": [ "hashes": [
@ -462,7 +475,6 @@
"sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8",
"sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5" "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" "version": "==1.1.0"
}, },
"jinja2": { "jinja2": {
@ -474,51 +486,78 @@
}, },
"markupsafe": { "markupsafe": {
"hashes": [ "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": { "more-itertools": {
"hashes": [ "hashes": [
"sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4",
"sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc",
"sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d" "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"
], ],
"version": "==4.3.0" "version": "==5.0.0"
}, },
"multidict": { "multidict": {
"hashes": [ "hashes": [
"sha256:05eeab69bf2b0664644c62bd92fabb045163e5b8d4376a31dfb52ce0210ced7b", "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
"sha256:0c85880efa7cadb18e3b5eef0aa075dc9c0a3064cbbaef2e20be264b9cf47a64", "sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3",
"sha256:136f5a4a6a4adeacc4dc820b8b22f0a378fb74f326e259c54d1817639d1d40a0", "sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef",
"sha256:14906ad3347c7d03e9101749b16611cf2028547716d0840838d3c5e2b3b0f2d3", "sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b",
"sha256:1ade4a3b71b1bf9e90c5f3d034a87fe4949c087ef1f6cd727fdd766fe8bbd121", "sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73",
"sha256:22939a00a511a59f9ecc0158b8db728afef57975ce3782b3a265a319d05b9b12", "sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc",
"sha256:2b86b02d872bc5ba5b3a4530f6a7ba0b541458ab4f7c1429a12ac326231203f7", "sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3",
"sha256:3c11e92c3dfc321014e22fb442bc9eb70e01af30d6ce442026b0c35723448c66", "sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd",
"sha256:4ba3bd26f282b201fdbce351f1c5d17ceb224cbedb73d6e96e6ce391b354aacc", "sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351",
"sha256:4c6e78d042e93751f60672989efbd6a6bc54213ed7ff695fff82784bbb9ea035", "sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941",
"sha256:4d80d1901b89cc935a6cf5b9fd89df66565272722fe2e5473168927a9937e0ca", "sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d",
"sha256:4fcf71d33178a00cc34a57b29f5dab1734b9ce0f1c97fb34666deefac6f92037", "sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1",
"sha256:52f7670b41d4b4d97866ebc38121de8bcb9813128b7c4942b07794d08193c0ab", "sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b",
"sha256:5368e2b7649a26b7253c6c9e53241248aab9da49099442f5be238fde436f18c9", "sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a",
"sha256:5bb65fbb48999044938f0c0508e929b14a9b8bf4939d8263e9ea6691f7b54663", "sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3",
"sha256:60672bb5577472800fcca1ac9dae232d1461db9f20f055184be8ce54b0052572", "sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7",
"sha256:669e9be6d148fc0283f53e17dd140cde4dc7c87edac8319147edd5aa2a830771", "sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0",
"sha256:6a0b7a804e8d1716aa2c72e73210b48be83d25ba9ec5cf52cf91122285707bb1", "sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0",
"sha256:79034ea3da3cf2a815e3e52afdc1f6c1894468c98bdce5d2546fa2342585497f", "sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014",
"sha256:79247feeef6abcc11137ad17922e865052f23447152059402fc320f99ff544bb", "sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5",
"sha256:81671c2049e6bf42c7fd11a060f8bc58f58b7b3d6f3f951fc0b15e376a6a5a98", "sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036",
"sha256:82ac4a5cb56cc9280d4ae52c2d2ebcd6e0668dd0f9ef17f0a9d7c82bd61e24fa", "sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d",
"sha256:9436267dbbaa49dad18fbbb54f85386b0f5818d055e7b8e01d219661b6745279", "sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a",
"sha256:94e4140bb1343115a1afd6d84ebf8fca5fb7bfb50e1c2cbd6f2fb5d3117ef102", "sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce",
"sha256:a2cab366eae8a0ffe0813fd8e335cf0d6b9bb6c5227315f53bb457519b811537", "sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1",
"sha256:a596019c3eafb1b0ae07db9f55a08578b43c79adb1fe1ab1fd818430ae59ee6f", "sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a",
"sha256:e8848ae3cd6a784c29fae5055028bee9bffcc704d8bcad09bd46b42b44a833e2", "sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9",
"sha256:e8a048bfd7d5a280f27527d11449a509ddedf08b58a09a24314828631c099306", "sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7",
"sha256:f6dd28a0ac60e2426a6918f36f1b4e2620fc785a0de7654cd206ba842eee57fd" "sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b"
], ],
"version": "==4.4.2" "version": "==4.5.2"
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
@ -529,48 +568,45 @@
}, },
"pluggy": { "pluggy": {
"hashes": [ "hashes": [
"sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1", "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616",
"sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1" "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.8.1"
"version": "==0.7.1"
}, },
"py": { "py": {
"hashes": [ "hashes": [
"sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1", "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694",
"sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6" "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.7.0"
"version": "==1.6.0"
}, },
"pygments": { "pygments": {
"hashes": [ "hashes": [
"sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
"sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
], ],
"version": "==2.2.0" "version": "==2.3.1"
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
"sha256:bc6c7146b91af3f567cf6daeaec360bc07d45ffec4cf5353f4d7a208ce7ca30a", "sha256:40856e74d4987de5d01761a22d1621ae1c7f8774585acae358aa5c5936c6c90b",
"sha256:d29593d8ebe7b57d6967b62494f8c72b03ac0262b1eed63826c6f788b3606401" "sha256:f353aab21fd474459d97b709e527b5571314ee5f067441dc9f88e33eecd96592"
], ],
"version": "==2.2.2" "version": "==2.3.0"
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:7e258ee50338f4e46957f9e09a0f10fb1c2d05493fa901d113a8dafd0790de4e", "sha256:3e65a22eb0d4f1bdbc1eacccf4a3198bf8d4049dea5112d70a0c61b00e748d02",
"sha256:9332147e9af2dcf46cd7ceb14d5acadb6564744ddff1fe8c17f0ce60ece7d9a2" "sha256:5924060b374f62608a078494b909d341720a050b5224ff87e17e12377486a71d"
], ],
"version": "==3.8.2" "version": "==4.1.0"
}, },
"pytest-asyncio": { "pytest-asyncio": {
"hashes": [ "hashes": [
"sha256:a962e8e1b6ec28648c8fe214edab4e16bacdb37b52df26eb9d63050af309b2a9", "sha256:9fac5100fd716cbecf6ef89233e8590a4ad61d729d1732e0a96b84182df1daaf",
"sha256:fbd92c067c16111174a1286bfb253660f1e564e5146b39eeed1133315cf2c2cf" "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.10.0"
"version": "==0.9.0"
}, },
"python-levenshtein": { "python-levenshtein": {
"hashes": [ "hashes": [
@ -580,10 +616,10 @@
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
"sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
], ],
"version": "==2018.5" "version": "==2018.9"
}, },
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
@ -603,10 +639,10 @@
}, },
"raven": { "raven": {
"hashes": [ "hashes": [
"sha256:3fd787d19ebb49919268f06f19310e8112d619ef364f7989246fc8753d469888", "sha256:3fa6de6efa2493a7c827472e984ce9b020797d0da16f1db67197bcc23c8fae54",
"sha256:95f44f3ea2c1b176d5450df4becdb96c15bf2632888f9ab193e9dd22300ce46a" "sha256:44a13f87670836e153951af9a3c80405d36b43097db869a36e92809673692ce4"
], ],
"version": "==6.9.0" "version": "==6.10.0"
}, },
"raven-aiohttp": { "raven-aiohttp": {
"hashes": [ "hashes": [
@ -617,10 +653,10 @@
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
"sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
], ],
"version": "==2.19.1" "version": "==2.21.0"
}, },
"schema": { "schema": {
"hashes": [ "hashes": [
@ -631,10 +667,10 @@
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
], ],
"version": "==1.11.0" "version": "==1.12.0"
}, },
"snowballstemmer": { "snowballstemmer": {
"hashes": [ "hashes": [
@ -645,17 +681,17 @@
}, },
"sphinx": { "sphinx": {
"hashes": [ "hashes": [
"sha256:217a7705adcb573da5bbe1e0f5cab4fa0bd89fd9342c9159121746f593c2d5a4", "sha256:429e3172466df289f0f742471d7e30ba3ee11f3b5aecd9a840480d03f14bcfe5",
"sha256:a602513f385f1d5785ff1ca420d9c7eb1a1b63381733b2f0ea8188a391314a86" "sha256:c4cb17ba44acffae3d3209646b6baec1e215cad3065e852c68cc569d4df1b9f8"
], ],
"version": "==1.7.9" "version": "==1.8.3"
}, },
"sphinx-rtd-theme": { "sphinx-rtd-theme": {
"hashes": [ "hashes": [
"sha256:3b49758a64f8a1ebd8a33cb6cc9093c3935a908b716edfaa5772fd86aac27ef6", "sha256:02f02a676d6baabb758a20c7a479d58648e0f64f13e07d1b388e9bb2afe86a09",
"sha256:80e01ec0eb711abacb1fa507f3eae8b805ae8fa3e8b057abfdf497e3f644c82c" "sha256:d0f6bc70f98961145c5b0e26a992829363a197321ba571b31b24ea91879e0c96"
], ],
"version": "==0.4.1" "version": "==0.4.2"
}, },
"sphinxcontrib-asyncio": { "sphinxcontrib-asyncio": {
"hashes": [ "hashes": [
@ -668,39 +704,36 @@
"sha256:68ca7ff70785cbe1e7bccc71a48b5b6d965d79ca50629606c7861a21b206d9dd", "sha256:68ca7ff70785cbe1e7bccc71a48b5b6d965d79ca50629606c7861a21b206d9dd",
"sha256:9de47f375baf1ea07cdb3436ff39d7a9c76042c10a769c52353ec46e4e8fc3b9" "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" "version": "==1.1.0"
}, },
"toml": { "toml": {
"hashes": [ "hashes": [
"sha256:380178cde50a6a79f9d2cf6f42a62a5174febe5eea4126fe4038785f1d888d42", "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:a7901919d3e4f92ffba7ff40a9d697e35bbbc8a8049fe8da742f34c83606d957" "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
], ],
"version": "==0.9.6" "version": "==0.10.0"
}, },
"tox": { "tox": {
"hashes": [ "hashes": [
"sha256:7f802b37fffd3b5ef2aab104943fa5dad24bf9564bb7e732e54b8d0cfec2fca0", "sha256:2a8d8a63660563e41e64e3b5b677e81ce1ffa5e2a93c2c565d3768c287445800",
"sha256:cc97859bd7f38aa5b3b8ba55ffe7ee9952e7050faad1aedc0829cd3db2fb61d6" "sha256:edfca7809925f49bdc110d0a2d9966bbf35a0c25637216d9586e7a5c5de17bfb"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.4.0" "version": "==3.6.1"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" "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.24.1"
"version": "==1.23"
}, },
"virtualenv": { "virtualenv": {
"hashes": [ "hashes": [
"sha256:2ce32cd126117ce2c539f0134eb89de91a8413a29baac49cbab3eb50e2026669", "sha256:34b9ae3742abed2f95d3970acf4d80533261d6061b51160b197f84e5b4c98b4c",
"sha256:ca07b4c0b54e14a91af9f34d0919790b016923d157afda5efdde55c96718f752" "sha256:fa736831a7b18bd2bfeef746beb622a92509e9733d645952da136b0639cd40cd"
], ],
"markers": "python_version != '3.0.*' and python_version >= '2.7' and python_version != '3.2.*' and python_version != '3.1.*'", "version": "==16.2.0"
"version": "==16.0.0"
}, },
"websockets": { "websockets": {
"hashes": [ "hashes": [
@ -726,23 +759,23 @@
"sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff", "sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff",
"sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454" "sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454"
], ],
"markers": "python_version >= '3.4'",
"version": "==6.0" "version": "==6.0"
}, },
"yarl": { "yarl": {
"hashes": [ "hashes": [
"sha256:2556b779125621b311844a072e0ed367e8409a18fa12cbd68eb1258d187820f9", "sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9",
"sha256:4aec0769f1799a9d4496827292c02a7b1f75c0bab56ab2b60dd94ebb57cbd5ee", "sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f",
"sha256:55369d95afaacf2fa6b49c84d18b51f1704a6560c432a0f9a1aeb23f7b971308", "sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb",
"sha256:6c098b85442c8fe3303e708bbb775afd0f6b29f77612e8892627bcab4b939357", "sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320",
"sha256:9182cd6f93412d32e009020a44d6d170d2093646464a88aeec2aef50592f8c78", "sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842",
"sha256:c8cbc21bbfa1dd7d5386d48cc814fe3d35b80f60299cdde9279046f399c3b0d8", "sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0",
"sha256:db6f70a4b09cde813a4807843abaaa60f3b15fb4a2a06f9ae9c311472662daa1", "sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829",
"sha256:f17495e6fe3d377e3faac68121caef6f974fcb9e046bc075bcff40d8e5cc69a4", "sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310",
"sha256:f85900b9cca0c67767bb61b2b9bd53208aaa7373dae633dbe25d179b4bf38aa7" "sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4",
"sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8",
"sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1"
], ],
"markers": "python_version >= '3.4.1'", "version": "==1.3.0"
"version": "==1.2.6"
} }
} }
} }

View File

@ -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

View File

@ -230,7 +230,6 @@ class Cleanup(commands.Cog):
to_delete = await self.get_messages_for_deletion( to_delete = await self.get_messages_for_deletion(
channel=channel, number=None, after=after, delete_pinned=delete_pinned channel=channel, number=None, after=after, delete_pinned=delete_pinned
) )
to_delete.append(ctx.message)
reason = "{}({}) deleted {} messages in channel {}.".format( reason = "{}({}) deleted {} messages in channel {}.".format(
author.name, author.id, len(to_delete), channel.name author.name, author.id, len(to_delete), channel.name

View File

@ -358,7 +358,6 @@ class CustomCommands(commands.Cog):
result = responses result = responses
else: else:
continue continue
# Replace newlines with spaces
# Cut preview to 52 characters max # Cut preview to 52 characters max
if len(result) > 52: if len(result) > 52:
result = result[:49] + "..." result = result[:49] + "..."
@ -369,7 +368,8 @@ class CustomCommands(commands.Cog):
results.append((f"{ctx.clean_prefix}{command}", result)) results.append((f"{ctx.clean_prefix}{command}", result))
if await ctx.embed_requested(): 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)) pages = list(pagify(content, page_length=1024))
embed_pages = [] embed_pages = []
for idx, page in enumerate(pages, start=1): for idx, page in enumerate(pages, start=1):

View File

@ -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)

View File

@ -11,12 +11,11 @@ from discord.ext.commands.errors import BadArgument
from redbot.core import checks, Config, modlog, commands from redbot.core import checks, Config, modlog, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.i18n import Translator, cog_i18n from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import box, escape, pagify from redbot.core.utils.chat_formatting import box, escape, pagify, format_perms_list
from .checks import mod_or_voice_permissions, admin_or_voice_permissions, bot_has_voice_permissions 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 redbot.core.utils.mod import is_mod_or_superior, is_allowed_by_hierarchy, get_audit_reason
from .log import log from .log import log
from redbot.core.utils.common_filters import filter_invites, filter_various_mentions
_ = T_ = Translator("Mod", __file__) _ = T_ = Translator("Mod", __file__)
@ -794,15 +793,60 @@ class Mod(commands.Cog):
except discord.HTTPException: except discord.HTTPException:
return 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.command()
@commands.guild_only() @commands.guild_only()
@admin_or_voice_permissions(mute_members=True, deafen_members=True) @checks.admin_or_permissions(mute_members=True, deafen_members=True)
@bot_has_voice_permissions(mute_members=True, deafen_members=True)
async def voiceban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): 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.""" """Ban a user from speaking and listening in the server's voice channels."""
user_voice_state = user.voice user_voice_state: discord.VoiceState = user.voice
if user_voice_state is None: if (
await ctx.send(_("No voice state for that user!")) await self._voice_perm_check(
ctx, user_voice_state, deafen_members=True, mute_members=True
)
is False
):
return return
needs_mute = True if user_voice_state.mute is False else False needs_mute = True if user_voice_state.mute is False else False
needs_deafen = True if user_voice_state.deaf 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.command()
@commands.guild_only() @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): 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.""" """Unban a user from speaking and listening in the server's voice channels."""
user_voice_state = user.voice user_voice_state = user.voice
if user_voice_state is None: if (
await ctx.send(_("No voice state for that user!")) await self._voice_perm_check(
ctx, user_voice_state, deafen_members=True, mute_members=True
)
is False
):
return return
needs_unmute = True if user_voice_state.mute else False needs_unmute = True if user_voice_state.mute else False
needs_undeafen = True if user_voice_state.deaf else False needs_undeafen = True if user_voice_state.deaf else False
@ -925,47 +971,43 @@ class Mod(commands.Cog):
@mute.command(name="voice") @mute.command(name="voice")
@commands.guild_only() @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): async def voice_mute(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
"""Mute a user in their current voice channel.""" """Mute a user in their current voice channel."""
user_voice_state = user.voice 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 guild = ctx.guild
author = ctx.author author = ctx.author
if user_voice_state: channel = user_voice_state.channel
channel = user_voice_state.channel audit_reason = get_audit_reason(author, reason)
if 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: if success:
await ctx.send( await ctx.send(
_("Muted {user} in channel {channel.name}").format( _("Muted {user} in channel {channel.name}").format(user=user, channel=channel)
user=user, channel=channel )
) try:
) await modlog.create_case(
try: self.bot,
await modlog.create_case( guild,
self.bot, ctx.message.created_at,
guild, "vmute",
ctx.message.created_at, user,
"vmute", author,
user, reason,
author, until=None,
reason, channel=channel,
until=None, )
channel=channel, except RuntimeError as e:
) await ctx.send(e)
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!"))
else: else:
await ctx.send(_("No voice state for the target!")) await ctx.send(issue)
return
@mute.command(name="channel") @mute.command(name="channel")
@commands.guild_only() @commands.guild_only()
@ -1081,51 +1123,45 @@ class Mod(commands.Cog):
@unmute.command(name="voice") @unmute.command(name="voice")
@commands.guild_only() @commands.guild_only()
@mod_or_voice_permissions(mute_members=True)
@bot_has_voice_permissions(mute_members=True)
async def unmute_voice( async def unmute_voice(
self, ctx: commands.Context, user: discord.Member, *, reason: str = None self, ctx: commands.Context, user: discord.Member, *, reason: str = None
): ):
"""Unmute a user in their current voice channel.""" """Unmute a user in their current voice channel."""
user_voice_state = user.voice 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 guild = ctx.guild
author = ctx.author author = ctx.author
if user_voice_state: channel = user_voice_state.channel
channel = user_voice_state.channel audit_reason = get_audit_reason(author, reason)
if channel:
audit_reason = get_audit_reason(author, reason)
success, message = await self.unmute_user( success, message = await self.unmute_user(guild, channel, author, user, audit_reason)
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,
) )
except RuntimeError as e:
if success: await ctx.send(e)
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!"))
else: else:
await ctx.send(_("No voice state for the target!")) await ctx.send(_("Unmute failed. Reason: {}").format(message))
return
@checks.mod_or_permissions(administrator=True) @checks.mod_or_permissions(administrator=True)
@unmute.command(name="channel") @unmute.command(name="channel")
@ -1347,8 +1383,8 @@ class Mod(commands.Cog):
user = author user = author
# A special case for a special someone :^) # A special case for a special someone :^)
special_date = datetime(2016, 1, 10, 6, 8, 4, 443_000) special_date = datetime(2016, 1, 10, 6, 8, 4, 443000)
is_special = user.id == 96_130_341_705_637_888 and guild.id == 133_049_272_517_001_216 is_special = user.id == 96130341705637888 and guild.id == 133049272517001216
roles = sorted(user.roles)[1:] roles = sorted(user.roles)[1:]
names, nicks = await self.get_names_and_nicks(user) names, nicks = await self.get_names_and_nicks(user)

View File

@ -148,5 +148,5 @@ class VersionInfo:
) )
__version__ = "3.0.0rc2" __version__ = "3.0.0rc3.post1"
version_info = VersionInfo.from_str(__version__) version_info = VersionInfo.from_str(__version__)

View File

@ -111,7 +111,7 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin):
self.main_dir = bot_dir 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) super().__init__(*args, formatter=Help(), **kwargs)

View File

@ -3,7 +3,7 @@ import pkgutil
from importlib import import_module, invalidate_caches from importlib import import_module, invalidate_caches
from importlib.machinery import ModuleSpec from importlib.machinery import ModuleSpec
from pathlib import Path from pathlib import Path
from typing import Tuple, Union, List, Optional from typing import Union, List, Optional
import redbot.cogs import redbot.cogs
from redbot.core.utils import deduplicate_iterables from redbot.core.utils import deduplicate_iterables
@ -25,8 +25,6 @@ class NoSuchCog(ImportError):
Different from ImportError because some ImportErrors can happen inside cogs. Different from ImportError because some ImportErrors can happen inside cogs.
""" """
pass
class CogManager: class CogManager:
"""Directory manager for Red's cogs. """Directory manager for Red's cogs.
@ -39,30 +37,27 @@ class CogManager:
CORE_PATH = Path(redbot.cogs.__path__[0]) CORE_PATH = Path(redbot.cogs.__path__[0])
def __init__(self, paths: Tuple[str] = ()): def __init__(self):
self.conf = Config.get_conf(self, 2938473984732, True) self.conf = Config.get_conf(self, 2938473984732, True)
tmp_cog_install_path = cog_data_path(self) / "cogs" tmp_cog_install_path = cog_data_path(self) / "cogs"
tmp_cog_install_path.mkdir(parents=True, exist_ok=True) tmp_cog_install_path.mkdir(parents=True, exist_ok=True)
self.conf.register_global(paths=[], install_path=str(tmp_cog_install_path)) 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, ...]: async def paths(self) -> List[Path]:
"""Get all currently valid path directories. """Get all currently valid path directories, in order of priority
Returns Returns
------- -------
`tuple` of `pathlib.Path` List[pathlib.Path]
All valid cog paths. 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()] return deduplicate_iterables(
other_paths = self._paths [await self.install_path()], await self.user_defined_paths(), [self.CORE_PATH]
)
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())
async def install_path(self) -> Path: async def install_path(self) -> Path:
"""Get the install path for 3rd party cogs. """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. The path to the directory where 3rd party cogs are stored.
""" """
p = Path(await self.conf.install_path()) return Path(await self.conf.install_path()).resolve()
return p.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: async def set_install_path(self, path: Path) -> Path:
"""Set the install path for 3rd party cogs. """Set the install path for 3rd party cogs.
@ -125,11 +132,10 @@ class CogManager:
path = Path(path) path = Path(path)
return 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. """Add a cog path to current list.
This will ignore duplicates. Does have a side effect of removing all This will ignore duplicates.
invalid paths from the saved path list.
Parameters Parameters
---------- ----------
@ -156,11 +162,12 @@ class CogManager:
if path == self.CORE_PATH: if path == self.CORE_PATH:
raise ValueError("Cannot add the core path as an additional path.") raise ValueError("Cannot add the core path as an additional path.")
async with self.conf.paths() as paths: current_paths = await self.user_defined_paths()
if not any(Path(p) == path for p in paths): if path not in current_paths:
paths.append(str(path)) 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. """Remove a path from the current paths list.
Parameters Parameters
@ -168,20 +175,12 @@ class CogManager:
path : `pathlib.Path` or `str` path : `pathlib.Path` or `str`
Path to remove. Path to remove.
Returns
-------
`tuple` of `pathlib.Path`
Tuple of new valid paths.
""" """
path = self._ensure_path_obj(path).resolve() path = self._ensure_path_obj(path).resolve()
paths = await self.user_defined_paths()
paths = [Path(p) for p in await self.conf.paths()] paths.remove(path)
if path in paths: await self.set_paths(paths)
paths.remove(path)
await self.set_paths(paths)
return tuple(paths)
async def set_paths(self, paths_: List[Path]): async def set_paths(self, paths_: List[Path]):
"""Set the current paths list. """Set the current paths list.
@ -192,7 +191,7 @@ class CogManager:
List of paths to set. 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) await self.conf.paths.set(str_paths)
async def _find_ext_cog(self, name: str) -> ModuleSpec: async def _find_ext_cog(self, name: str) -> ModuleSpec:
@ -213,9 +212,9 @@ class CogManager:
------ ------
NoSuchCog NoSuchCog
When no cog with the requested name was found. When no cog with the requested name was found.
""" """
resolved_paths = await self.paths() real_paths = list(map(str, [await self.install_path()] + await self.user_defined_paths()))
real_paths = [str(p) for p in resolved_paths if p != self.CORE_PATH]
for finder, module_name, _ in pkgutil.iter_modules(real_paths): for finder, module_name, _ in pkgutil.iter_modules(real_paths):
if name == module_name: if name == module_name:
@ -287,10 +286,8 @@ class CogManager:
return await self._find_core_cog(name) return await self._find_core_cog(name)
async def available_modules(self) -> List[str]: async def available_modules(self) -> List[str]:
"""Finds the names of all available modules to load. """Finds the names of all available modules to load."""
""" paths = list(map(str, await self.paths()))
paths = (await self.install_path(),) + await self.paths()
paths = [str(p) for p in paths]
ret = [] ret = []
for finder, module_name, _ in pkgutil.iter_modules(paths): for finder, module_name, _ in pkgutil.iter_modules(paths):
@ -314,13 +311,6 @@ _ = Translator("CogManagerUI", __file__)
class CogManagerUI(commands.Cog): class CogManagerUI(commands.Cog):
"""Commands to interface with Red's cog manager.""" """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() @commands.command()
@checks.is_owner() @checks.is_owner()
async def paths(self, ctx: commands.Context): async def paths(self, ctx: commands.Context):
@ -330,8 +320,7 @@ class CogManagerUI(commands.Cog):
cog_mgr = ctx.bot.cog_mgr cog_mgr = ctx.bot.cog_mgr
install_path = await cog_mgr.install_path() install_path = await cog_mgr.install_path()
core_path = cog_mgr.CORE_PATH core_path = cog_mgr.CORE_PATH
cog_paths = await cog_mgr.paths() cog_paths = await cog_mgr.user_defined_paths()
cog_paths = [p for p in cog_paths if p not in (install_path, core_path)]
msg = _("Install Path: {install_path}\nCore Path: {core_path}\n\n").format( msg = _("Install Path: {install_path}\nCore Path: {core_path}\n\n").format(
install_path=install_path, core_path=core_path install_path=install_path, core_path=core_path
@ -369,7 +358,11 @@ class CogManagerUI(commands.Cog):
from !paths from !paths
""" """
path_number -= 1 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: try:
to_remove = cog_paths.pop(path_number) to_remove = cog_paths.pop(path_number)
except IndexError: except IndexError:
@ -388,8 +381,11 @@ class CogManagerUI(commands.Cog):
# Doing this because in the paths command they're 1 indexed # Doing this because in the paths command they're 1 indexed
from_ -= 1 from_ -= 1
to -= 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: try:
to_move = all_paths.pop(from_) to_move = all_paths.pop(from_)
except IndexError: except IndexError:

View File

@ -145,7 +145,7 @@ class Command(CogCommandMixin, commands.Command):
@property @property
def parents(self) -> List["Group"]: 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. 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. If the command has no parents, this will be an empty list.

View File

@ -1173,6 +1173,9 @@ class Core(commands.Cog, CoreLogic):
await ctx.send( await ctx.send(
_("A backup has been made of this instance. It is at {}.").format(backup_file) _("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)")) await ctx.send(_("Would you like to receive a copy via DM? (y/n)"))
pred = MessagePredicate.yes_or_no(ctx) pred = MessagePredicate.yes_or_no(ctx)
@ -1183,10 +1186,18 @@ class Core(commands.Cog, CoreLogic):
else: else:
if pred.result is True: if pred.result is True:
await ctx.send(_("OK, it's on its way!")) await ctx.send(_("OK, it's on its way!"))
async with ctx.author.typing(): try:
await ctx.author.send( async with ctx.author.typing():
_("Here's a copy of the backup"), file=discord.File(str(backup_file)) 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: else:
await ctx.send(_("OK then.")) await ctx.send(_("OK then."))
else: else:

View File

@ -15,7 +15,7 @@ from pkg_resources import DistributionNotFound
from . import __version__ as red_version, version_info as red_version_info, VersionInfo, commands from . import __version__ as red_version, version_info as red_version_info, VersionInfo, commands
from .data_manager import storage_type 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 from .utils import fuzzy_command_search, format_fuzzy_results
log = logging.getLogger("red") log = logging.getLogger("red")
@ -234,18 +234,13 @@ def init_events(bot, cli_flags):
else: else:
await ctx.send(await format_fuzzy_results(ctx, fuzzy_commands, embed=False)) await ctx.send(await format_fuzzy_results(ctx, fuzzy_commands, embed=False))
elif isinstance(error, commands.BotMissingPermissions): elif isinstance(error, commands.BotMissingPermissions):
missing_perms: List[str] = [] if bin(error.missing.value).count("1") == 1: # Only one perm missing
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:
plural = "" plural = ""
else: else:
plural = "s" plural = "s"
await ctx.send( await ctx.send(
"I require the {perms} permission{plural} to execute that command.".format( "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): elif isinstance(error, commands.CheckFailure):

View File

@ -1,5 +1,8 @@
import itertools import itertools
from typing import Sequence, Iterator, List from typing import Sequence, Iterator, List
import discord
from redbot.core.i18n import Translator from redbot.core.i18n import Translator
_ = Translator("UtilsChatFormatting", __file__) _ = Translator("UtilsChatFormatting", __file__)
@ -329,7 +332,7 @@ def escape(text: str, *, mass_mentions: bool = False, formatting: bool = False)
return text 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*. """Get comma-separted list, with the last element joined with *and*.
This uses an Oxford comma, because without one, items containing This uses an Oxford comma, because without one, items containing
@ -357,3 +360,29 @@ def humanize_list(items: Sequence[str]):
if len(items) == 1: if len(items) == 1:
return items[0] return items[0]
return ", ".join(items[:-1]) + _(", and ") + items[-1] 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")

View File

@ -73,10 +73,13 @@ async def menu(
# noinspection PyAsyncCall # noinspection PyAsyncCall
start_adding_reactions(message, controls.keys(), ctx.bot.loop) start_adding_reactions(message, controls.keys(), ctx.bot.loop)
else: else:
if isinstance(current_page, discord.Embed): try:
await message.edit(embed=current_page) if isinstance(current_page, discord.Embed):
else: await message.edit(embed=current_page)
await message.edit(content=current_page) else:
await message.edit(content=current_page)
except discord.NotFound:
return
try: try:
react, user = await ctx.bot.wait_for( react, user = await ctx.bot.wait_for(
@ -90,9 +93,12 @@ async def menu(
except discord.Forbidden: # cannot remove all reactions except discord.Forbidden: # cannot remove all reactions
for key in controls.keys(): for key in controls.keys():
await message.remove_reaction(key, ctx.bot.user) await message.remove_reaction(key, ctx.bot.user)
return None except discord.NotFound:
return
return await controls[react.emoji](ctx, pages, controls, message, page, timeout, react.emoji) else:
return await controls[react.emoji](
ctx, pages, controls, message, page, timeout, react.emoji
)
async def next_page( async def next_page(
@ -106,10 +112,8 @@ async def next_page(
): ):
perms = message.channel.permissions_for(ctx.me) perms = message.channel.permissions_for(ctx.me)
if perms.manage_messages: # Can manage messages, so remove react if perms.manage_messages: # Can manage messages, so remove react
try: with contextlib.suppress(discord.NotFound):
await message.remove_reaction(emoji, ctx.author) await message.remove_reaction(emoji, ctx.author)
except discord.NotFound:
pass
if page == len(pages) - 1: if page == len(pages) - 1:
page = 0 # Loop around to the first item page = 0 # Loop around to the first item
else: else:
@ -128,10 +132,8 @@ async def prev_page(
): ):
perms = message.channel.permissions_for(ctx.me) perms = message.channel.permissions_for(ctx.me)
if perms.manage_messages: # Can manage messages, so remove react if perms.manage_messages: # Can manage messages, so remove react
try: with contextlib.suppress(discord.NotFound):
await message.remove_reaction(emoji, ctx.author) await message.remove_reaction(emoji, ctx.author)
except discord.NotFound:
pass
if page == 0: if page == 0:
page = len(pages) - 1 # Loop around to the last item page = len(pages) - 1 # Loop around to the last item
else: else:
@ -148,9 +150,8 @@ async def close_menu(
timeout: float, timeout: float,
emoji: str, emoji: str,
): ):
if message: with contextlib.suppress(discord.NotFound):
await message.delete() await message.delete()
return None
def start_adding_reactions( def start_adding_reactions(
@ -161,7 +162,7 @@ def start_adding_reactions(
"""Start adding reactions to a message. """Start adding reactions to a message.
This is a non-blocking operation - calling this will schedule the 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. execute asynchronously. There is no need to await this function.
This is particularly useful if you wish to start waiting for a 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 is exactly what `menu` uses to do that.
This spawns a `asyncio.Task` object and schedules it on ``loop``. 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`. `asyncio.get_event_loop`.
Parameters Parameters

View File

@ -2,7 +2,6 @@ import discord
from datetime import datetime from datetime import datetime
from redbot.core.utils.chat_formatting import pagify from redbot.core.utils.chat_formatting import pagify
import io import io
import sys
import weakref import weakref
from typing import List, Optional from typing import List, Optional
from .common_filters import filter_mass_mentions from .common_filters import filter_mass_mentions
@ -151,15 +150,12 @@ class Tunnel(metaclass=TunnelMeta):
""" """
files = [] files = []
size = 0 max_size = 8 * 1000 * 1000
max_size = 8 * 1024 * 1024 if m.attachments and sum(a.size for a in m.attachments) <= max_size:
for a in m.attachments: for a in m.attachments:
_fp = io.BytesIO() _fp = io.BytesIO()
await a.save(_fp) await a.save(_fp)
size += sys.getsizeof(_fp) files.append(discord.File(_fp, filename=a.filename))
if size > max_size:
return []
files.append(discord.File(_fp, filename=a.filename))
return files return files
async def communicate( async def communicate(

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import argparse import argparse
import asyncio import asyncio
import json import json
@ -177,26 +176,21 @@ def basic_setup():
async def json_to_mongo(current_data_dir: Path, storage_details: dict): async def json_to_mongo(current_data_dir: Path, storage_details: dict):
from redbot.core.drivers.red_mongo import Mongo from redbot.core.drivers.red_mongo import Mongo
core_data_file = list(current_data_dir.glob("core/settings.json"))[0] core_data_file = current_data_dir / "core" / "settings.json"
m = Mongo("Core", "0", **storage_details) driver = Mongo(cog_name="Core", identifier="0", **storage_details)
with core_data_file.open(mode="r") as f: with core_data_file.open(mode="r") as f:
core_data = json.loads(f.read()) core_data = json.loads(f.read())
collection = m.get_collection() data = core_data.get("0", {})
await collection.update_one( for key, value in data.items():
{"_id": m.unique_cog_identifier}, update={"$set": core_data["0"]}, upsert=True await driver.set(key, value=value)
)
for p in current_data_dir.glob("cogs/**/settings.json"): for p in current_data_dir.glob("cogs/**/settings.json"):
cog_name = p.parent.stem
with p.open(mode="r") as f: with p.open(mode="r") as f:
cog_data = json.loads(f.read()) cog_data = json.load(f)
cog_i = None for identifier, data in cog_data.items():
for ident in list(cog_data.keys()): driver = Mongo(cog_name, identifier, **storage_details)
cog_i = str(hash(ident)) for key, value in data.items():
cog_m = Mongo(p.parent.stem, cog_i, **storage_details) await driver.set(key, value=value)
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
)
async def mongo_to_json(current_data_dir: Path, storage_details: dict): async def mongo_to_json(current_data_dir: Path, storage_details: dict):

View File

@ -9,59 +9,59 @@ install_requires = [
"aiohttp-json-rpc==0.11.2", "aiohttp-json-rpc==0.11.2",
"aiohttp==3.4.4", "aiohttp==3.4.4",
"appdirs==1.4.3", "appdirs==1.4.3",
"async-timeout==3.0.0", "async-timeout==3.0.1",
"attrs==18.2.0", "attrs==18.2.0",
"chardet==3.0.4", "chardet==3.0.4",
"colorama==0.3.9", "colorama==0.4.1",
"discord.py>=1.0.0a0", "discord.py>=1.0.0a0",
"distro==1.3.0; sys_platform == 'linux'", "distro==1.3.0; sys_platform == 'linux'",
"fuzzywuzzy==0.17.0", "fuzzywuzzy==0.17.0",
"idna-ssl==1.1.0", "idna-ssl==1.1.0",
"idna==2.7", "idna==2.8",
"multidict==4.4.2", "multidict==4.5.2",
"python-levenshtein==0.12.0", "python-levenshtein==0.12.0",
"pyyaml==3.13", "pyyaml==3.13",
"raven==6.9.0", "raven==6.10.0",
"raven-aiohttp==0.7.0", "raven-aiohttp==0.7.0",
"schema==0.6.8", "schema==0.6.8",
"websockets==6.0", "websockets==6.0",
"yarl==1.2.6", "yarl==1.3.0",
] ]
extras_require = { extras_require = {
"test": [ "test": [
"atomicwrites==1.2.1", "atomicwrites==1.2.1",
"more-itertools==4.3.0", "more-itertools==5.0.0",
"pluggy==0.7.1", "pluggy==0.8.1",
"py==1.6.0", "py==1.7.0",
"pytest==3.8.2", "pytest==4.1.0",
"pytest-asyncio==0.9.0", "pytest-asyncio==0.10.0",
"six==1.11.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": [ "docs": [
"alabaster==0.7.11", "alabaster==0.7.12",
"babel==2.6.0", "babel==2.6.0",
"certifi==2018.8.24", "certifi==2018.11.29",
"docutils==0.14", "docutils==0.14",
"imagesize==1.1.0", "imagesize==1.1.0",
"Jinja2==2.10", "Jinja2==2.10",
"MarkupSafe==1.0", "MarkupSafe==1.1.0",
"packaging==18.0", "packaging==18.0",
"pyparsing==2.2.2", "pyparsing==2.3.0",
"Pygments==2.2.0", "Pygments==2.3.1",
"pytz==2018.5", "pytz==2018.9",
"requests==2.19.1", "requests==2.21.0",
"urllib3==1.23", "six==1.12.0",
"six==1.11.0",
"snowballstemmer==1.2.1", "snowballstemmer==1.2.1",
"sphinx==1.7.9", "sphinx==1.8.3",
"sphinx_rtd_theme==0.4.1", "sphinx_rtd_theme==0.4.2",
"sphinxcontrib-asyncio==0.2.0", "sphinxcontrib-asyncio==0.2.0",
"sphinxcontrib-websupport==1.1.0", "sphinxcontrib-websupport==1.1.0",
"urllib3==1.24.1",
], ],
"voice": ["red-lavalink==0.1.2"], "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" python_requires = ">=3.6.2,<3.8"