diff --git a/.gitignore b/.gitignore
index 2ba771e..c9e9eda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -109,3 +109,5 @@ attachments/*
.DS_Store
.vscode
+.idea
+*.iml
diff --git a/Pipfile.lock b/Pipfile.lock
index 787e283..a389bd0 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -208,10 +208,10 @@
},
"hypercorn": {
"hashes": [
- "sha256:9ab2d6b686e5a9a455433144a269d3004ad0aa079d15ed291554858a9b8dbd38",
- "sha256:9afb874f8332645a00b77311d1975ac6690fe37e3c268d9d65f5250a3134ee29"
+ "sha256:0562810a44f3e5aa0b28d42adacbbf7185f2975bb305733e6de71d6a927c8d7b",
+ "sha256:3e9ee14deb34215907364adb62694b36abc471b8eaaf22d812eb85757e5479ad"
],
- "version": "==0.6.0"
+ "version": "==0.7.0"
},
"hyperframe": {
"hashes": [
@@ -318,35 +318,52 @@
},
"pillow": {
"hashes": [
- "sha256:15c056bfa284c30a7f265a41ac4cbbc93bdbfc0dfe0613b9cb8a8581b51a9e55",
- "sha256:1a4e06ba4f74494ea0c58c24de2bb752818e9d504474ec95b0aa94f6b0a7e479",
- "sha256:1c3c707c76be43c9e99cb7e3d5f1bee1c8e5be8b8a2a5eeee665efbf8ddde91a",
- "sha256:1fd0b290203e3b0882d9605d807b03c0f47e3440f97824586c173eca0aadd99d",
- "sha256:24114e4a6e1870c5a24b1da8f60d0ba77a0b4027907860188ea82bd3508c80eb",
- "sha256:258d886a49b6b058cd7abb0ab4b2b85ce78669a857398e83e8b8e28b317b5abb",
- "sha256:33c79b6dd6bc7f65079ab9ca5bebffb5f5d1141c689c9c6a7855776d1b09b7e8",
- "sha256:367385fc797b2c31564c427430c7a8630db1a00bd040555dfc1d5c52e39fcd72",
- "sha256:3c1884ff078fb8bf5f63d7d86921838b82ed4a7d0c027add773c2f38b3168754",
- "sha256:44e5240e8f4f8861d748f2a58b3f04daadab5e22bfec896bf5434745f788f33f",
- "sha256:46aa988e15f3ea72dddd81afe3839437b755fffddb5e173886f11460be909dce",
- "sha256:74d90d499c9c736d52dd6d9b7221af5665b9c04f1767e35f5dd8694324bd4601",
- "sha256:809c0a2ce9032cbcd7b5313f71af4bdc5c8c771cb86eb7559afd954cab82ebb5",
- "sha256:85d1ef2cdafd5507c4221d201aaf62fc9276f8b0f71bd3933363e62a33abc734",
- "sha256:8c3889c7681af77ecfa4431cd42a2885d093ecb811e81fbe5e203abc07e0995b",
- "sha256:9218d81b9fca98d2c47d35d688a0cea0c42fd473159dfd5612dcb0483c63e40b",
- "sha256:9aa4f3827992288edd37c9df345783a69ef58bd20cc02e64b36e44bcd157bbf1",
- "sha256:9d80f44137a70b6f84c750d11019a3419f409c944526a95219bea0ac31f4dd91",
- "sha256:b7ebd36128a2fe93991293f997e44be9286503c7530ace6a55b938b20be288d8",
- "sha256:c4c78e2c71c257c136cdd43869fd3d5e34fc2162dc22e4a5406b0ebe86958239",
- "sha256:c6a842537f887be1fe115d8abb5daa9bc8cc124e455ff995830cc785624a97af",
- "sha256:cf0a2e040fdf5a6d95f4c286c6ef1df6b36c218b528c8a9158ec2452a804b9b8",
- "sha256:cfd28aad6fc61f7a5d4ee556a997dc6e5555d9381d1390c00ecaf984d57e4232",
- "sha256:dca5660e25932771460d4688ccbb515677caaf8595f3f3240ec16c117deff89a",
- "sha256:de7aedc85918c2f887886442e50f52c1b93545606317956d65f342bd81cb4fc3",
- "sha256:e6c0bbf8e277b74196e3140c35f9a1ae3eafd818f7f2d3a15819c49135d6c062"
+ "sha256:0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de",
+ "sha256:0ab7c5b5d04691bcbd570658667dd1e21ca311c62dcfd315ad2255b1cd37f64f",
+ "sha256:0b3e6cf3ea1f8cecd625f1420b931c83ce74f00c29a0ff1ce4385f99900ac7c4",
+ "sha256:0c6ce6ae03a50b0306a683696234b8bc88c5b292d4181ae365b89bd90250ab08",
+ "sha256:1454ee7297a81c8308ad61d74c849486efa1badc543453c4b90db0bf99decc1c",
+ "sha256:23efd7f83f2ad6036e2b9ef27a46df7e333de1ad9087d341d87e12225d0142b2",
+ "sha256:365c06a45712cd723ec16fa4ceb32ce46ad201eb7bbf6d3c16b063c72b61a3ed",
+ "sha256:38301fbc0af865baa4752ddae1bb3cbb24b3d8f221bf2850aad96b243306fa03",
+ "sha256:3aef1af1a91798536bbab35d70d35750bd2884f0832c88aeb2499aa2d1ed4992",
+ "sha256:3c86051d41d1c8b28b9dde08ac93e73aa842991995b12771b0af28da49086bbf",
+ "sha256:3fe0ab49537d9330c9bba7f16a5f8b02da615b5c809cdf7124f356a0f182eccd",
+ "sha256:406c856e0f6fc330322a319457d9ff6162834050cda2cf1eaaaea4b771d01914",
+ "sha256:45a619d5c1915957449264c81c008934452e3fd3604e36809212300b2a4dab68",
+ "sha256:49f90f147883a0c3778fd29d3eb169d56416f25758d0f66775db9184debc8010",
+ "sha256:504f5334bfd974490a86fef3e3b494cd3c332a8a680d2f258ca03388b40ae230",
+ "sha256:51fe9cfcd32c849c6f36ca293648f279fc5097ca8dd6e518b10df3a6a9a13431",
+ "sha256:571b5a758baf1cb6a04233fb23d6cf1ca60b31f9f641b1700bfaab1194020555",
+ "sha256:5ac381e8b1259925287ccc5a87d9cf6322a2dc88ae28a97fe3e196385288413f",
+ "sha256:6052a9e9af4a9a2cc01da4bbee81d42d33feca2bde247c4916d8274b12bb31a4",
+ "sha256:6153db744a743c0c8c91b8e3b9d40e0b13a5d31dbf8a12748c6d9bfd3ddc01ad",
+ "sha256:6fd63afd14a16f5d6b408f623cc2142917a1f92855f0df997e09a49f0341be8a",
+ "sha256:70acbcaba2a638923c2d337e0edea210505708d7859b87c2bd81e8f9902ae826",
+ "sha256:70b1594d56ed32d56ed21a7fbb2a5c6fd7446cdb7b21e749c9791eac3a64d9e4",
+ "sha256:76638865c83b1bb33bcac2a61ce4d13c17dba2204969dedb9ab60ef62bede686",
+ "sha256:7b2ec162c87fc496aa568258ac88631a2ce0acfe681a9af40842fc55deaedc99",
+ "sha256:7b403ea842b70c4fa0a4969a5d8d86e932c941095b7cda077ea68f7b98ead30b",
+ "sha256:7be698a28175eae5354da94f5f3dc787d5efae6aca7ad1f286a781afde6a27dd",
+ "sha256:7cee2cef07c8d76894ebefc54e4bb707dfc7f258ad155bd61d87f6cd487a70ff",
+ "sha256:7d16d4498f8b374fc625c4037742fbdd7f9ac383fd50b06f4df00c81ef60e829",
+ "sha256:82840783842b27933cc6388800cb547f31caf436f7e23384d456bdf5fc8dfe49",
+ "sha256:8755e600b33f4e8c76a590b42acc35d24f4dc801a5868519ce569b9462d77598",
+ "sha256:9159285ab4030c6f85e001468cb5886de05e6bd9304e9e7d46b983f7d2fad0cc",
+ "sha256:b50bc1780681b127e28f0075dfb81d6135c3a293e0c1d0211133c75e2179b6c0",
+ "sha256:b5aa19f1da16b4f5e47b6930053f08cba77ceccaed68748061b0ec24860e510c",
+ "sha256:bd0582f831ad5bcad6ca001deba4568573a4675437db17c4031939156ff339fa",
+ "sha256:cdd53acd3afb9878a2289a1b55807871f9877c81174ae0d3763e52f907131d25",
+ "sha256:cfd40d8a4b59f7567620410f966bb1f32dc555b2b19f82a91b147fac296f645c",
+ "sha256:e150c5aed6e67321edc6893faa6701581ca2d393472f39142a00e551bcd249a5",
+ "sha256:e3ae410089de680e8f84c68b755b42bc42c0ceb8c03dbea88a5099747091d38e",
+ "sha256:e403b37c6a253ebca5d0f2e5624643997aaae529dc96299162418ef54e29eb70",
+ "sha256:e9046e559c299b395b39ac7dbf16005308821c2f24a63cae2ab173bd6aa11616",
+ "sha256:ef6be704ae2bc8ad0ebc5cb850ee9139493b0fc4e81abcc240fb392a63ebc808",
+ "sha256:f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b"
],
"index": "pypi",
- "version": "==6.0.0"
+ "version": "==6.1.0"
},
"pycparser": {
"hashes": [
@@ -354,12 +371,6 @@
],
"version": "==2.19"
},
- "pytoml": {
- "hashes": [
- "sha256:ca2d0cb127c938b8b76a9a0d0f855cf930c1d50cc3a0af6d3595b566519a1013"
- ],
- "version": "==0.1.20"
- },
"quart": {
"hashes": [
"sha256:06ad6c71f12e6f64625bebd4e591903fa653740df4b0e50de2e8722a6370011e",
@@ -382,13 +393,21 @@
],
"version": "==2.1.0"
},
+ "toml": {
+ "hashes": [
+ "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
+ "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"
+ ],
+ "version": "==0.10.0"
+ },
"typing-extensions": {
"hashes": [
- "sha256:07b2c978670896022a43c4b915df8958bec4a6b84add7f2c87b2b728bda3ba64",
- "sha256:f3f0e67e1d42de47b5c67c32c9b26641642e9170fe7e292991793705cd5fef7c",
- "sha256:fb2cd053238d33a8ec939190f30cfd736c00653a85a2919415cecf7dc3d9da71"
+ "sha256:2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95",
+ "sha256:b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87",
+ "sha256:d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed"
],
- "version": "==3.7.2"
+ "version": "==3.7.4"
},
"websockets": {
"hashes": [
@@ -491,35 +510,35 @@
},
"importlib-metadata": {
"hashes": [
- "sha256:a9f185022cfa69e9ca5f7eabfd5a58b689894cb78a11e3c8c89398a8ccbb8e7f",
- "sha256:df1403cd3aebeb2b1dcd3515ca062eecb5bd3ea7611f18cba81130c68707e879"
+ "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7",
+ "sha256:cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db"
],
- "version": "==0.17"
+ "version": "==0.18"
},
"more-itertools": {
"hashes": [
- "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
- "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
+ "sha256:3ad685ff8512bf6dc5a8b82ebf73543999b657eded8c11803d9ba6b648986f4d",
+ "sha256:8bb43d1f51ecef60d81854af61a3a880555a14643691cc4b64a6ee269c78f09a"
],
"markers": "python_version > '2.7'",
- "version": "==7.0.0"
+ "version": "==7.1.0"
},
"mypy": {
"hashes": [
- "sha256:2afe51527b1f6cdc4a5f34fc90473109b22bf7f21086ba3e9451857cf11489e6",
- "sha256:56a16df3e0abb145d8accd5dbb70eba6c4bd26e2f89042b491faa78c9635d1e2",
- "sha256:5764f10d27b2e93c84f70af5778941b8f4aa1379b2430f85c827e0f5464e8714",
- "sha256:5bbc86374f04a3aa817622f98e40375ccb28c4836f36b66706cf3c6ccce86eda",
- "sha256:6a9343089f6377e71e20ca734cd8e7ac25d36478a9df580efabfe9059819bf82",
- "sha256:6c9851bc4a23dc1d854d3f5dfd5f20a016f8da86bcdbb42687879bb5f86434b0",
- "sha256:b8e85956af3fcf043d6f87c91cbe8705073fc67029ba6e22d3468bfee42c4823",
- "sha256:b9a0af8fae490306bc112229000aa0c2ccc837b49d29a5c42e088c132a2334dd",
- "sha256:bbf643528e2a55df2c1587008d6e3bda5c0445f1240dfa85129af22ae16d7a9a",
- "sha256:c46ab3438bd21511db0f2c612d89d8344154c0c9494afc7fbc932de514cf8d15",
- "sha256:f7a83d6bd805855ef83ec605eb01ab4fa42bcef254b13631e451cbb44914a9b0"
+ "sha256:0107bff4f46a289f0e4081d59b77cef1c48ea43da5a0dbf0005d54748b26df2a",
+ "sha256:07957f5471b3bb768c61f08690c96d8a09be0912185a27a68700f3ede99184e4",
+ "sha256:10af62f87b6921eac50271e667cc234162a194e742d8e02fc4ddc121e129a5b0",
+ "sha256:11fd60d2f69f0cefbe53ce551acf5b1cec1a89e7ce2d47b4e95a84eefb2899ae",
+ "sha256:15e43d3b1546813669bd1a6ec7e6a11d2888db938e0607f7b5eef6b976671339",
+ "sha256:352c24ba054a89bb9a35dd064ee95ab9b12903b56c72a8d3863d882e2632dc76",
+ "sha256:437020a39417e85e22ea8edcb709612903a9924209e10b3ec6d8c9f05b79f498",
+ "sha256:49925f9da7cee47eebf3420d7c0e00ec662ec6abb2780eb0a16260a7ba25f9c4",
+ "sha256:6724fcd5777aa6cebfa7e644c526888c9d639bd22edd26b2a8038c674a7c34bd",
+ "sha256:7a17613f7ea374ab64f39f03257f22b5755335b73251d0d253687a69029701ba",
+ "sha256:cdc1151ced496ca1496272da7fc356580e95f2682be1d32377c22ddebdf73c91"
],
"index": "pypi",
- "version": "==0.701"
+ "version": "==0.720"
},
"mypy-extensions": {
"hashes": [
@@ -575,34 +594,38 @@
},
"typed-ast": {
"hashes": [
- "sha256:132eae51d6ef3ff4a8c47c393a4ef5ebf0d1aecc96880eb5d6c8ceab7017cc9b",
- "sha256:18141c1484ab8784006c839be8b985cfc82a2e9725837b0ecfa0203f71c4e39d",
- "sha256:2baf617f5bbbfe73fd8846463f5aeafc912b5ee247f410700245d68525ec584a",
- "sha256:3d90063f2cbbe39177e9b4d888e45777012652d6110156845b828908c51ae462",
- "sha256:4304b2218b842d610aa1a1d87e1dc9559597969acc62ce717ee4dfeaa44d7eee",
- "sha256:4983ede548ffc3541bae49a82675996497348e55bafd1554dc4e4a5d6eda541a",
- "sha256:5315f4509c1476718a4825f45a203b82d7fdf2a6f5f0c8f166435975b1c9f7d4",
- "sha256:6cdfb1b49d5345f7c2b90d638822d16ba62dc82f7616e9b4caa10b72f3f16649",
- "sha256:7b325f12635598c604690efd7a0197d0b94b7d7778498e76e0710cd582fd1c7a",
- "sha256:8d3b0e3b8626615826f9a626548057c5275a9733512b137984a68ba1598d3d2f",
- "sha256:8f8631160c79f53081bd23446525db0bc4c5616f78d04021e6e434b286493fd7",
- "sha256:912de10965f3dc89da23936f1cc4ed60764f712e5fa603a09dd904f88c996760",
- "sha256:b010c07b975fe853c65d7bbe9d4ac62f1c69086750a574f6292597763781ba18",
- "sha256:c908c10505904c48081a5415a1e295d8403e353e0c14c42b6d67f8f97fae6616",
- "sha256:c94dd3807c0c0610f7c76f078119f4ea48235a953512752b9175f9f98f5ae2bd",
- "sha256:ce65dee7594a84c466e79d7fb7d3303e7295d16a83c22c7c4037071b059e2c21",
- "sha256:eaa9cfcb221a8a4c2889be6f93da141ac777eb8819f077e1d09fb12d00a09a93",
- "sha256:f3376bc31bad66d46d44b4e6522c5c21976bf9bca4ef5987bb2bf727f4506cbb",
- "sha256:f9202fa138544e13a4ec1a6792c35834250a85958fde1251b6a22e07d1260ae7"
+ "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
+ "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
+ "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
+ "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
+ "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
+ "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
+ "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
+ "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
+ "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
+ "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
+ "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
+ "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
+ "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
+ "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
+ "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
],
- "version": "==1.3.5"
+ "version": "==1.4.0"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95",
+ "sha256:b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87",
+ "sha256:d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed"
+ ],
+ "version": "==3.7.4"
},
"zipp": {
"hashes": [
- "sha256:8c1019c6aad13642199fbe458275ad6a84907634cc9f0989877ccc4a2840139d",
- "sha256:ca943a7e809cc12257001ccfb99e3563da9af99d52f261725e96dfe0f9275bc3"
+ "sha256:4970c3758f4e89a7857a973b1e2a5d75bcdc47794442f2e2dd4fe8e0466e809a",
+ "sha256:8a5712cfd3bb4248015eb3b0b3c54a5f6ee3f2425963ef2a0125b8bc40aafaec"
],
- "version": "==0.5.1"
+ "version": "==0.5.2"
}
}
}
diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py
index 771fbfd..389ec85 100644
--- a/litecord/blueprints/channels.py
+++ b/litecord/blueprints/channels.py
@@ -182,8 +182,7 @@ async def close_channel(channel_id):
main_tbl = {
ChannelType.GUILD_TEXT: 'guild_text_channels',
ChannelType.GUILD_VOICE: 'guild_voice_channels',
-
- # TODO: categories?
+ ChannelType.GUILD_CATEGORY: None,
}[ctype]
await _update_func(guild_id, channel_id)
@@ -195,10 +194,17 @@ async def close_channel(channel_id):
await delete_messages(channel_id)
await guild_cleanup(channel_id)
- await app.db.execute(f"""
- DELETE FROM {main_tbl}
- WHERE id = $1
- """, channel_id)
+ if main_tbl is not None:
+ await app.db.execute(f"""
+ DELETE FROM {main_tbl}
+ WHERE id = $1
+ """, channel_id)
+
+ if ctype == ChannelType.GUILD_CATEGORY:
+ await app.db.execute("""
+ UPDATE guild_channels SET parent_id = NULL
+ WHERE parent_id = $1
+ """, channel_id)
await app.db.execute("""
DELETE FROM guild_channels
diff --git a/litecord/blueprints/guild/channels.py b/litecord/blueprints/guild/channels.py
index 2b0467b..ef45640 100644
--- a/litecord/blueprints/guild/channels.py
+++ b/litecord/blueprints/guild/channels.py
@@ -20,18 +20,16 @@ along with this program. If not, see .
from quart import Blueprint, request, current_app as app, jsonify
from litecord.blueprints.auth import token_check
-from litecord.snowflake import get_snowflake
-from litecord.errors import BadRequest
-from litecord.enums import ChannelType
-from litecord.blueprints.guild.roles import gen_pairs
-
-from litecord.schemas import (
- validate, ROLE_UPDATE_POSITION, CHAN_CREATE
-)
from litecord.blueprints.checks import (
guild_check, guild_owner_check, guild_perm_check
)
-
+from litecord.blueprints.guild.roles import gen_pairs
+from litecord.enums import ChannelType
+from litecord.errors import BadRequest
+from litecord.schemas import (
+ validate, CHANNEL_UPDATE_POSITION, CHAN_CREATE
+)
+from litecord.snowflake import get_snowflake
bp = Blueprint('guild_channels', __name__)
@@ -52,6 +50,8 @@ async def _specific_chan_create(channel_id, ctype, **kwargs):
kwargs.get('bitrate', 64),
kwargs.get('user_limit', 0)
)
+ elif ctype == ChannelType.GUILD_CATEGORY and 'parent_id' in kwargs and kwargs['parent_id'] is not None:
+ raise BadRequest('Guild category cannot have a parent!')
async def create_guild_channel(guild_id: int, channel_id: int,
@@ -72,11 +72,13 @@ async def create_guild_channel(guild_id: int, channel_id: int,
# account for the first channel in a guild too
max_pos = max_pos or 0
+ parent_id = kwargs['parent_id'] if 'parent_id' in kwargs else None
+
# all channels go to guild_channels
await app.db.execute("""
- INSERT INTO guild_channels (id, guild_id, name, position)
- VALUES ($1, $2, $3, $4)
- """, channel_id, guild_id, kwargs['name'], max_pos + 1)
+ INSERT INTO guild_channels (id, guild_id, parent_id, name, position)
+ VALUES ($1, $2, $3, $4, $5)
+ """, channel_id, guild_id, parent_id, kwargs['name'], max_pos + 1)
# the rest of sql magic is dependant on the channel
# we're creating (a text or voice or category),
@@ -107,7 +109,8 @@ async def create_channel(guild_id):
channel_type = ChannelType(channel_type)
if channel_type not in (ChannelType.GUILD_TEXT,
- ChannelType.GUILD_VOICE):
+ ChannelType.GUILD_VOICE,
+ ChannelType.GUILD_CATEGORY):
raise BadRequest('Invalid channel type')
new_channel_id = get_snowflake()
@@ -138,39 +141,37 @@ async def _chan_update_dispatch(guild_id: int, channel_id: int):
await app.dispatcher.dispatch_guild(guild_id, 'CHANNEL_UPDATE', chan)
-async def _do_single_swap(guild_id: int, pair: tuple):
- """Do a single channel swap, dispatching
- the CHANNEL_UPDATE events for after the swap"""
- pair1, pair2 = pair
- channel_1, new_pos_1 = pair1
- channel_2, new_pos_2 = pair2
-
- # do the swap in a transaction.
- conn = await app.db.acquire()
-
- async with conn.transaction():
- await conn.executemany("""
- UPDATE guild_channels
- SET position = $1
- WHERE id = $2 AND guild_id = $3
- """, [
- (new_pos_1, channel_1, guild_id),
- (new_pos_2, channel_2, guild_id)])
-
- await app.db.release(conn)
-
- await _chan_update_dispatch(guild_id, channel_1)
- await _chan_update_dispatch(guild_id, channel_2)
-
-
-async def _do_channel_swaps(guild_id: int, swap_pairs: list):
- """Swap channel pairs' positions, given the list
- of pairs to do.
+async def _do_channel_updates(guild_id: int, updates: list):
+ """Update channel positions, given the list of pairs to do.
Dispatches CHANNEL_UPDATEs to the guild.
"""
- for pair in swap_pairs:
- await _do_single_swap(guild_id, pair)
+ updated = []
+
+ conn = await app.db.acquire()
+ for pair in updates:
+ _id, pos = pair
+
+ async with conn.transaction():
+ await conn.execute("""
+ UPDATE guild_channels
+ SET position = $1
+ WHERE id = $2 AND guild_id = $3
+ """, pos, _id, guild_id)
+ updated.append(_id)
+
+ await app.db.release(conn)
+
+ for _id in updated:
+ await _chan_update_dispatch(guild_id, _id)
+
+
+def _group_channel(chan):
+ if ChannelType(chan['type']) == ChannelType.GUILD_CATEGORY:
+ return 'c'
+ elif chan['parent_id'] is None:
+ return 'n'
+ return chan['parent_id']
@bp.route('//channels', methods=['PATCH'])
@@ -181,22 +182,42 @@ async def modify_channel_pos(guild_id):
await guild_owner_check(user_id, guild_id)
await guild_perm_check(user_id, guild_id, 'manage_channels')
- # same thing as guild.roles, so we use
- # the same schema and all.
raw_j = await request.get_json()
- j = validate({'roles': raw_j}, ROLE_UPDATE_POSITION)
- j = j['roles']
+ j = validate({'channels': raw_j}, CHANNEL_UPDATE_POSITION)
+ j = j['channels']
- channels = await app.storage.get_channel_data(guild_id)
+ channels = {int(chan['id']): chan for chan in await app.storage.get_channel_data(guild_id)}
+ channel_tree = {}
- channel_positions = {chan['position']: int(chan['id'])
- for chan in channels}
+ for chan in j:
+ conn = await app.db.acquire()
+ _id = int(chan['id'])
+ if _id in channels and 'parent_id' in chan and (chan['parent_id'] is None or chan['parent_id'] in channels):
+ channels[_id]['parent_id'] = chan['parent_id']
+ await conn.execute("""
+ UPDATE guild_channels
+ SET parent_id = $1
+ WHERE id = $2 AND guild_id = $3
+ """, chan['parent_id'], chan['id'], guild_id)
- swap_pairs = gen_pairs(
- j,
- channel_positions
- )
+ await _chan_update_dispatch(guild_id, chan['id'])
+ await app.db.release(conn)
- await _do_channel_swaps(guild_id, swap_pairs)
+ for chan in channels.values():
+ channel_tree.setdefault(_group_channel(chan), []).append(chan)
+
+ for _key in channel_tree:
+ _channels = channel_tree[_key]
+ _channel_ids = list(map(lambda chan: int(chan['id']), _channels))
+ print(_key, _channel_ids)
+ _channel_positions = {chan['position']: int(chan['id'])
+ for chan in _channels}
+ _change_list = list(filter(lambda chan: 'position' in chan and int(chan['id']) in _channel_ids, j))
+ _swap_pairs = gen_pairs(
+ _change_list,
+ _channel_positions
+ )
+
+ await _do_channel_updates(guild_id, _swap_pairs)
return '', 204
diff --git a/litecord/blueprints/guild/roles.py b/litecord/blueprints/guild/roles.py
index 5dfcbe7..7d98f87 100644
--- a/litecord/blueprints/guild/roles.py
+++ b/litecord/blueprints/guild/roles.py
@@ -19,21 +19,19 @@ along with this program. If not, see .
from typing import List, Dict, Tuple
-from quart import Blueprint, request, current_app as app, jsonify
from logbook import Logger
+from quart import Blueprint, request, current_app as app, jsonify
from litecord.auth import token_check
-
from litecord.blueprints.checks import (
guild_check, guild_perm_check
)
+from litecord.permissions import get_role_perms
from litecord.schemas import (
validate, ROLE_CREATE, ROLE_UPDATE, ROLE_UPDATE_POSITION
)
-
from litecord.snowflake import get_snowflake
from litecord.utils import dict_get
-from litecord.permissions import get_role_perms
DEFAULT_EVERYONE_PERMS = 104324161
log = Logger(__name__)
@@ -152,39 +150,33 @@ async def _role_update_dispatch(role_id: int, guild_id: int):
async def _role_pairs_update(guild_id: int, pairs: list):
- """Update the roles' positions.
+ """Update the role positions.
Dispatches GUILD_ROLE_UPDATE for all roles being updated.
"""
- for pair in pairs:
- pair_1, pair_2 = pair
+ updated = []
+ conn = await app.db.acquire()
- role_1, new_pos_1 = pair_1
- role_2, new_pos_2 = pair_2
+ async with conn.transaction():
+ for pair in pairs:
+ _id, pos = pair
- conn = await app.db.acquire()
- async with conn.transaction():
# update happens in a transaction
# so we don't fuck it up
await conn.execute("""
UPDATE roles
SET position = $1
WHERE roles.id = $2
- """, new_pos_1, role_1)
+ """, pos, _id)
+ updated.append(_id)
- await conn.execute("""
- UPDATE roles
- SET position = $1
- WHERE roles.id = $2
- """, new_pos_2, role_2)
+ await app.db.release(conn)
- await app.db.release(conn)
+ for _id in updated:
+ await _role_update_dispatch(_id, guild_id)
- # the route fires multiple Guild Role Update.
- await _role_update_dispatch(role_1, guild_id)
- await _role_update_dispatch(role_2, guild_id)
+PairList = List[Tuple[int, int]]
-PairList = List[Tuple[Tuple[int, int], Tuple[int, int]]]
def gen_pairs(list_of_changes: List[Dict[str, int]],
current_state: Dict[int, int],
@@ -192,23 +184,6 @@ def gen_pairs(list_of_changes: List[Dict[str, int]],
"""Generate a list of pairs that, when applied to the database,
will generate the desired state given in list_of_changes.
- We must check if the given list_of_changes isn't overwriting an
- element's (such as a role or a channel) position to an existing one,
- without there having an already existing change for the other one.
-
- Here's a pratical explanation with roles:
-
- R1 (in position RP1) wants to be in the same position
- as R2 (currently in position RP2).
-
- So, if we did the simpler approach, list_of_changes
- would just contain the preferred change: (R1, RP2).
-
- With gen_pairs, there MUST be a (R2, RP1) in list_of_changes,
- if there is, the given result in gen_pairs will be a pair
- ((R1, RP2), (R2, RP1)) which is then used to actually
- update the roles' positions in a transaction.
-
Parameters
----------
list_of_changes:
@@ -224,52 +199,37 @@ def gen_pairs(list_of_changes: List[Dict[str, int]],
Returns
-------
list
- List of swaps to do to achieve the preferred
+ List of changes to do to achieve the preferred
state given by ``list_of_changes``.
"""
pairs = []
blacklist = blacklist or []
- preferred_state = {element['id']: element['position']
- for element in list_of_changes}
+ preferred_state = []
+ for chan in current_state:
+ preferred_state.insert(chan, current_state[chan])
for blacklisted_id in blacklist:
- preferred_state.pop(blacklisted_id)
+ if blacklisted_id in preferred_state:
+ preferred_state.remove(blacklisted_id)
+
+ current_state = preferred_state.copy()
# for each change, we must find a matching change
# in the same list, so we can make a swap pair
for change in list_of_changes:
- element_1, new_pos_1 = change['id'], change['position']
-
- # check current pairs
- # so we don't repeat an element
- flag = False
-
- for pair in pairs:
- if (element_1, new_pos_1) in pair:
- flag = True
-
- # skip if found
- if flag:
+ _id, pos = change['id'], change['position']
+ if _id not in preferred_state:
continue
- # search if there is a role/channel in the
- # position we want to change to
- element_2 = current_state.get(new_pos_1)
+ preferred_state.remove(_id)
+ preferred_state.insert(pos, _id)
- if element_2 is None:
- continue
+ assert len(current_state) == len(preferred_state)
- # if there is, is that existing channel being
- # swapped to another position?
- new_pos_2 = preferred_state.get(element_2)
-
- # if its being swapped to leave space, add it
- # to the pairs list
- if new_pos_2 is not None:
- pairs.append(
- ((element_1, new_pos_1), (element_2, new_pos_2))
- )
+ for i in range(len(current_state)):
+ if current_state[i] != preferred_state[i]:
+ pairs.append((preferred_state[i], i))
return pairs
diff --git a/litecord/schemas.py b/litecord/schemas.py
index 47ea60f..37454cb 100644
--- a/litecord/schemas.py
+++ b/litecord/schemas.py
@@ -58,7 +58,6 @@ def _in_enum(enum, value) -> bool:
except ValueError:
return False
-
class LitecordValidator(Validator):
"""Main validator class for Litecord, containing custom types."""
def _validate_type_username(self, value: str) -> bool:
@@ -413,6 +412,22 @@ ROLE_UPDATE_POSITION = {
}
+CHANNEL_UPDATE_POSITION = {
+ 'channels': {
+ 'type': 'list',
+ 'schema': {
+ 'type': 'dict',
+ 'schema': {
+ 'id': {'coerce': int},
+ 'position': {'coerce': int},
+ 'parent_id': {'coerce': int, 'required': False, 'nullable': True},
+ 'lock_permissions': {'type': 'boolean', 'required': False},
+ },
+ }
+ }
+}
+
+
MEMBER_UPDATE = {
'nick': {
'type': 'nickname', 'required': False},
diff --git a/litecord/storage.py b/litecord/storage.py
index 79d07dd..2ae969f 100644
--- a/litecord/storage.py
+++ b/litecord/storage.py
@@ -381,6 +381,9 @@ class Storage:
return {**row, **dict(vrow)}
+ if chan_type == ChannelType.GUILD_CATEGORY:
+ return row
+
log.warning('unknown channel type: {}', chan_type)
return row
@@ -478,6 +481,8 @@ class Storage:
res['permission_overwrites'] = await self.chan_overwrites(
channel_id)
+ if (res['parent_id']) is not None:
+ res['parent_id'] = str(res['parent_id'])
res['id'] = str(res['id'])
return res
elif ctype == ChannelType.DM:
@@ -561,6 +566,8 @@ class Storage:
# Making sure.
res['id'] = str(res['id'])
+ if (res['parent_id']) is not None:
+ res['parent_id'] = str(res['parent_id'])
channels.append(res)
return channels