Implement channel categories and fix channel/role ordering

This commit is contained in:
gabixdev 2019-07-20 01:56:56 +02:00
parent 409d3fcc8f
commit 7e36e6f2c6
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
7 changed files with 247 additions and 213 deletions

2
.gitignore vendored
View File

@ -109,3 +109,5 @@ attachments/*
.DS_Store
.vscode
.idea
*.iml

185
Pipfile.lock generated
View File

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

View File

@ -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,11 +194,18 @@ async def close_channel(channel_id):
await delete_messages(channel_id)
await guild_cleanup(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
WHERE id = $1

View File

@ -20,18 +20,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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('/<int:guild_id>/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)
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_swaps(guild_id, swap_pairs)
await _do_channel_updates(guild_id, _swap_pairs)
return '', 204

View File

@ -19,21 +19,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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
role_1, new_pos_1 = pair_1
role_2, new_pos_2 = pair_2
updated = []
conn = await app.db.acquire()
async with conn.transaction():
for pair in pairs:
_id, pos = pair
# 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)
await conn.execute("""
UPDATE roles
SET position = $1
WHERE roles.id = $2
""", new_pos_2, role_2)
""", pos, _id)
updated.append(_id)
await app.db.release(conn)
# the route fires multiple Guild Role Update.
await _role_update_dispatch(role_1, guild_id)
await _role_update_dispatch(role_2, guild_id)
for _id in updated:
await _role_update_dispatch(_id, 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

View File

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

View File

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