From 21da0e0d3ac23eb50ee81689c209823fb456ad89 Mon Sep 17 00:00:00 2001 From: Luna Mendes Date: Sat, 23 Jun 2018 17:19:22 -0300 Subject: [PATCH] blueprints.guild: add implementations for more routes - Pipfile: add Cerberus for data validation - blueprints.guilds: add guild_owner_check - blueprints.guilds: simplify GUILD_MEMBER_UPDATE dispatch - litecord: add schemas.py - run: get json of errors --- Pipfile | 1 + Pipfile.lock | 238 +++++++--------------------------- litecord/blueprints/guilds.py | 167 ++++++++++++++++++++---- litecord/errors.py | 4 + litecord/schemas.py | 63 +++++++++ run.py | 9 +- 6 files changed, 265 insertions(+), 217 deletions(-) create mode 100644 litecord/schemas.py diff --git a/Pipfile b/Pipfile index 4c628ab..0371705 100644 --- a/Pipfile +++ b/Pipfile @@ -11,6 +11,7 @@ websockets = "==5.0.1" Quart = "==0.6.0" Earl-ETF = "==2.1.2" logbook = "*" +Cerberus = "==1.2" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 00cdcd8..0ed4449 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,20 @@ { "_meta": { "hash": { - "sha256": "ee2ebfbf41d6d6fbbb819a835e56ff70fd816601d4df9809b3c135484c8e1801" + "sha256": "bb7c65515934e11ebf3b17724f95fb8f1740a9c5aecb9bc2e8802bb54fa43574" + }, + "host-environment-markers": { + "implementation_name": "cpython", + "implementation_version": "3.6.5", + "os_name": "posix", + "platform_machine": "x86_64", + "platform_python_implementation": "CPython", + "platform_release": "4.17.2-1-ARCH", + "platform_system": "Linux", + "platform_version": "#1 SMP PREEMPT Sat Jun 16 11:08:59 UTC 2018", + "python_full_version": "3.6.5", + "python_version": "3.6", + "sys_platform": "linux" }, "pipfile-spec": 6, "requires": { @@ -17,266 +30,107 @@ }, "default": { "aiofiles": { - "hashes": [ - "sha256:25c66ea3872d05d53292a6b3f7fa0f86691512076446d83a505d227b5e76f668", - "sha256:852a493a877b73e11823bfd4e8e5ef2610d70d12c9eaed961bcd9124d8de8c10" - ], + "hashes": [], "version": "==0.3.2" }, "asyncpg": { - "hashes": [ - "sha256:166c8e094de78ccbfc598a5342037a6ca5d7ee1e8144b3cfade244dd591b1ed0", - "sha256:2913b7cffdfb5bf1da5ed751485b559d1f1990be005d6d63d3ca0bf09a9d8ee6", - "sha256:31d5a9d993ce97924d9601bf6a37bb8b542d63bc8716b36238511e5e5915b14c", - "sha256:440dc17ec98c2e69f58947a591eed5967724794c876b1d6e53950e9b0b561788", - "sha256:5791554375c71ef339ee01fafb931f593c9f3ec85a5db9753c185199cee6c87e", - "sha256:600e6e14078be26e2322dfded808af55248301633592a27337b192ca2137bf04", - "sha256:cca8de381ffca375dd7cbf13f918dd68ca493e9082e7fe3f5827c08e3cfd2432", - "sha256:d201b4851a39c1f2303d99f4199974d8e01d48cec7512b59e532979ba6277def", - "sha256:d70fee2708e538a7333bca94170da8ab9233e5eae136143e2275f7b2d9bb4c24", - "sha256:e0387c4a584394997335375e897b9d63a7a31a1c77482d8b94f9a1be77bcfd08", - "sha256:e6755dd3318c0b170d4727db0e310c26e569faa101c7506b6a3e041f16ef8df9" - ], - "index": "pypi", + "hashes": [], "version": "==0.16.0" }, "bcrypt": { - "hashes": [ - "sha256:01477981abf74e306e8ee31629a940a5e9138de000c6b0898f7f850461c4a0a5", - "sha256:054d6e0acaea429e6da3613fcd12d05ee29a531794d96f6ab959f29a39f33391", - "sha256:0872eeecdf9a429c1420158500eedb323a132bc5bf3339475151c52414729e70", - "sha256:09a3b8c258b815eadb611bad04ca15ec77d86aa9ce56070e1af0d5932f17642a", - "sha256:0f317e4ffbdd15c3c0f8ab5fbd86aa9aabc7bea18b5cc5951b456fe39e9f738c", - "sha256:2788c32673a2ad0062bea850ab73cffc0dba874db10d7a3682b6f2f280553f20", - "sha256:321d4d48be25b8d77594d8324c0585c80ae91ac214f62db9098734e5e7fb280f", - "sha256:346d6f84ff0b493dbc90c6b77136df83e81f903f0b95525ee80e5e6d5e4eef84", - "sha256:34dd60b90b0f6de94a89e71fcd19913a30e83091c8468d0923a93a0cccbfbbff", - "sha256:3b4c23300c4eded8895442c003ae9b14328ae69309ac5867e7530de8bdd7875d", - "sha256:43d1960e7db14042319c46925892d5fa99b08ff21d57482e6f5328a1aca03588", - "sha256:49e96267cd9be55a349fd74f9852eb9ae2c427cd7f6455d0f1765d7332292832", - "sha256:67ed1a374c9155ec0840214ce804616de49c3df9c5bc66740687c1c9b1cd9e8d", - "sha256:6efd9ca20aefbaf2e7e6817a2c6ed4a50ff6900fafdea1bcb1d0e9471743b144", - "sha256:8569844a5d8e1fdde4d7712a05ab2e6061343ac34af6e7e3d7935b2bd1907bfd", - "sha256:8629ea6a8a59f865add1d6a87464c3c676e60101b8d16ef404d0a031424a8491", - "sha256:988cac675e25133d01a78f2286189c1f01974470817a33eaf4cfee573cfb72a5", - "sha256:9a6fedda73aba1568962f7543a1f586051c54febbc74e87769bad6a4b8587c39", - "sha256:9eced8962ce3b7124fe20fd358cf8c7470706437fa064b9874f849ad4c5866fc", - "sha256:a005ed6163490988711ff732386b08effcbf8df62ae93dd1e5bda0714fad8afb", - "sha256:ae35dbcb6b011af6c840893b32399252d81ff57d52c13e12422e16b5fea1d0fb", - "sha256:b1e8491c6740f21b37cca77bc64677696a3fb9f32360794d57fa8477b7329eda", - "sha256:c906bdb482162e9ef48eea9f8c0d967acceb5c84f2d25574c7d2a58d04861df1", - "sha256:cb18ffdc861dbb244f14be32c47ab69604d0aca415bee53485fcea4f8e93d5ef", - "sha256:d86da365dda59010ba0d1ac45aa78390f56bf7f992e65f70b3b081d5e5257b09", - "sha256:e22f0997622e1ceec834fd25947dc2ee2962c2133ea693d61805bc867abaf7ea", - "sha256:f2fe545d27a619a552396533cddf70d83cecd880a611cdfdbb87ca6aec52f66b", - "sha256:f7fd3ed3745fe6e81e28dc3b3d76cce31525a91f32a387e1febd6b982caf8cdb", - "sha256:f9210820ee4818d84658ed7df16a7f30c9fba7d8b139959950acef91745cc0f7" - ], - "index": "pypi", + "hashes": [], "version": "==3.1.4" }, "blinker": { - "hashes": [ - "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6" - ], + "hashes": [], "version": "==1.4" }, + "cerberus": { + "hashes": [], + "version": "==1.2" + }, "cffi": { - "hashes": [ - "sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743", - "sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef", - "sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50", - "sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f", - "sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93", - "sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257", - "sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3", - "sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc", - "sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04", - "sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6", - "sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359", - "sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596", - "sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b", - "sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd", - "sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95", - "sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e", - "sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6", - "sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca", - "sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31", - "sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1", - "sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085", - "sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801", - "sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4", - "sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184", - "sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917", - "sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f", - "sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb" - ], + "hashes": [], "version": "==1.11.5" }, "click": { - "hashes": [ - "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", - "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" - ], + "hashes": [], "version": "==6.7" }, "earl-etf": { - "hashes": [ - "sha256:46bea44c3a3bf3914b01eb6a3bd42e3fb6dee6f45d68372b1237a153bf9fe4fc", - "sha256:52155ec379ec96560938f295bfa1f055743dc8be275423bdfebef14b16acfdab", - "sha256:6e3595ca9642d33cc5644c923826271f960c7df567fdaf62b5b439a0a6a9b619", - "sha256:870b40778ef6f644d6568a5afd481ce53bc87cb21fd92bfcf5b6072ede8e3078", - "sha256:d6f3e3bdbe2b74571f57b2c23f68233b464785ddf0c41870c3d8ff88db06b40e", - "sha256:f43cffbcf754e45f6716d82bb885c5072f3f8b16c1f1e26b46cb63f27e838640", - "sha256:f6a77049a21ed04095ef64f7e39ed26a2a88c17fffaf361119cd739683d25d55" - ], - "index": "pypi", + "hashes": [], "version": "==2.1.2" }, "h11": { - "hashes": [ - "sha256:1c0fbb1cba6f809fe3e6b27f8f6d517ca171f848922708871403636143d530d9", - "sha256:af77d5d82fa027c032650fb8afdef3cd0a3735ba01480bee908cddad9be1bdce" - ], + "hashes": [], "version": "==0.7.0" }, "h2": { - "hashes": [ - "sha256:4be613e35caad5680dc48f98f3bf4e7338c7c429e6375a5137be7fbe45219981", - "sha256:b2962f883fa392a23cbfcc4ad03c335bcc661be0cf9627657b589f0df2206e64" - ], + "hashes": [], "version": "==3.0.1" }, "hpack": { - "hashes": [ - "sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89", - "sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2" - ], + "hashes": [], "version": "==3.0.0" }, "hypercorn": { - "hashes": [ - "sha256:4c7cbc92e134d913d23815155fc190c8f52425fc2e0ce8131d192c310a43b91e", - "sha256:5a56a2e56f157516ea95fa589ee72d996894efba0530b411752d16e461af62e0" - ], + "hashes": [], "version": "==0.1.0" }, "hyperframe": { - "hashes": [ - "sha256:87567c9eb1540de1e7f48805adf00e87856409342fdebd0cd20cf5d381c38b69", - "sha256:a25944539db36d6a2e47689e7915dcee562b3f8d10c6cdfa0d53c91ed692fb04" - ], + "hashes": [], "version": "==5.1.0" }, "itsdangerous": { - "hashes": [ - "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" - ], - "index": "pypi", + "hashes": [], "version": "==0.24" }, "jinja2": { - "hashes": [ - "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", - "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" - ], + "hashes": [], "version": "==2.10" }, "logbook": { - "hashes": [ - "sha256:3c0a3ebd48e89fcdd725fe393eb9226c789dca5a4e7842d65e2f256645fd1cd9", - "sha256:97a827a49db0f543cf67a87dbbb7f3164383c801c28641630e200d00c396baa8", - "sha256:e273188067df6c82b09a0b51d1034628fcd2dab300a074c89e64c4dbbbec0328", - "sha256:f25f247eed81b7befca3a4646a266c7f6f328a7152d75223f9d3d0ee6476c015", - "sha256:f4d0b079756f99181d7d87b701b09af1f5d8b17110fe39465cc7afd94db1e95c" - ], - "index": "pypi", + "hashes": [], "version": "==1.4.0" }, "markupsafe": { - "hashes": [ - "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" - ], + "hashes": [], "version": "==1.0" }, "multidict": { - "hashes": [ - "sha256:1a1d76374a1e7fe93acef96b354a03c1d7f83e7512e225a527d283da0d7ba5e0", - "sha256:1d6e191965505652f194bc4c40270a842922685918a4f45e6936a6b15cc5816d", - "sha256:295961a6a88f1199e19968e15d9b42f3a191c89ec13034dbc212bf9c394c3c82", - "sha256:2be5af084de6c3b8e20d6421cb0346378a9c867dcf7c86030d6b0b550f9888e4", - "sha256:2eb99617c7a0e9f2b90b64bc1fb742611718618572747d6f3d6532b7b78755ab", - "sha256:4ba654c6b5ad1ae4a4d792abeb695b29ce981bb0f157a41d0fd227b385f2bef0", - "sha256:5ba766433c30d703f6b2c17eb0b6826c6f898e5f58d89373e235f07764952314", - "sha256:a59d58ee85b11f337b54933e8d758b2356fcdcc493248e004c9c5e5d11eedbe4", - "sha256:a6e35d28900cf87bcc11e6ca9e474db0099b78f0be0a41d95bef02d49101b5b2", - "sha256:b4df7ca9c01018a51e43937eaa41f2f5dce17a6382fda0086403bcb1f5c2cf8e", - "sha256:bbd5a6bffd3ba8bfe75b16b5e28af15265538e8be011b0b9fddc7d86a453fd4a", - "sha256:d870f399fcd58a1889e93008762a3b9a27cf7ea512818fc6e689f59495648355", - "sha256:e9404e2e19e901121c3c5c6cffd5a8ae0d1d67919c970e3b3262231175713068" - ], + "hashes": [], "version": "==4.3.1" }, "pycparser": { - "hashes": [ - "sha256:99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226" - ], + "hashes": [], "version": "==2.18" }, "quart": { - "hashes": [ - "sha256:a5f64f15ffa5e063c07ad3675c7fe82d3945a4d48c4e3f8d0bda0494c4964a1d", - "sha256:d6da4f0e99403918874ad9e7124f13f3c0a1bb4da10021c56a89433527909d52" - ], - "index": "pypi", + "hashes": [], "version": "==0.6.0" }, "six": { - "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - ], + "hashes": [], "version": "==1.11.0" }, "sortedcontainers": { - "hashes": [ - "sha256:607294c6e291a270948420f7ffa1fb3ed47384a4c08db6d1e9c92d08a6981982", - "sha256:ef38b128302ee8f65d81e31c9d8fbf10d81df4d6d06c9c0b66f01d33747525bb" - ], + "hashes": [], "version": "==2.0.4" }, + "typing": { + "hashes": [], + "version": "==3.6.4" + }, "typing-extensions": { - "hashes": [ - "sha256:1c0a8e3b4ce55207a03dd0dcb98bc47a704c71f14fe4311ec860cc8af8f4bd27", - "sha256:8b0962ecb92847974514b1724c8ae2b6dd1ffe86bcdfac429517f5e583ada658", - "sha256:be7b05ddab71727fabf1f071365043cf034e4cdac9cade1f1d61a6cc526aaafe" - ], + "hashes": [], "version": "==3.6.5" }, "websockets": { - "hashes": [ - "sha256:0b7b561bcbf992edd54e961b89551b5b6073415a0446fe445bd6554d41dabb95", - "sha256:2469c98f2254878a49a6eda248d3ed8a89bbdca85cc316ff72ea15924cec9e1f", - "sha256:29b676568e4fcb1a05064473b96243ef4e9391f251b4c485cf7f93507787b459", - "sha256:2a05e42400de009c1c330167cd6d90b300d2364d2dd1e6539d01a6a22901967b", - "sha256:39241fb291c1648e33dc41208be876a5771466291f0f6f7bff8f6732373084bd", - "sha256:43c332fc331541c57d40c124089b270d668c25a6b04908bd688969375db7327f", - "sha256:480259ec6e80f28859f23b5c231beb856fb96ab30e64ee621fdaf27da1515604", - "sha256:9049ec652713f5132b512d3498c2d37264580714ccc95dbc0f7f9622c3f6da7e", - "sha256:a17c45716178a42cc8f66f587507f01e169a75556749d88f714e4c1d295885d1", - "sha256:a49d315db5a7a19d55422e1678e8a1c3b9661d7296bef3179fa620cf80b12674", - "sha256:a911beb8149d7dae9d4c942927c448c05c41dfaa9c002a6bc26e269df932769b", - "sha256:cf34479130704797ce28a478f0b5985abe71ea90999a1c956e15fe0b0b11d0dc", - "sha256:d3724acff61ee1029fefc614cf005982338b033998a0b71fbb13a0a2fd99ab6f" - ], - "index": "pypi", + "hashes": [], "version": "==5.0.1" }, "wsproto": { - "hashes": [ - "sha256:02f214f6bb43cda62a511e2e8f1d5fa4703ed83d376d18d042bd2bbf2e995824", - "sha256:d2a7f718ab3144ec956a3267d57b5c172f0668827f5803e7d670837b0125b9fa" - ], + "hashes": [], "version": "==0.11.0" } }, diff --git a/litecord/blueprints/guilds.py b/litecord/blueprints/guilds.py index 1cfb4e4..dd51ad0 100644 --- a/litecord/blueprints/guilds.py +++ b/litecord/blueprints/guilds.py @@ -4,6 +4,7 @@ from ..auth import token_check from ..snowflake import get_snowflake from ..enums import ChannelType from ..errors import Forbidden, GuildNotFound, BadRequest +from ..schemas import validate, GUILD_UPDATE bp = Blueprint('guilds', __name__) @@ -20,6 +21,21 @@ async def guild_check(user_id: int, guild_id: int): raise GuildNotFound() +async def guild_owner_check(user_id: int, guild_id: int): + """Check if a user is the owner of the guild.""" + owner_id = await app.db.fetchval(""" + SELECT owner_id + FROM guilds + WHERE guild_id = $1 + """, guild_id) + + if not owner_id: + raise GuildNotFound() + + if user_id != owner_id: + raise Forbidden('You are not the owner of the guild') + + @bp.route('', methods=['POST']) async def create_guild(): user_id = await token_check() @@ -80,28 +96,82 @@ async def create_guild(): @bp.route('/', methods=['GET']) async def get_guild(guild_id): user_id = await token_check() + gj = await app.storage.get_guild(guild_id, user_id) gj_extra = await app.storage.get_guild_extra(guild_id, user_id, 250) return jsonify({**gj, **gj_extra}) +@bp.route('/', methods=['UPDATE']) +async def update_guild(guild_id): + user_id = await token_check() + await guild_check(user_id, guild_id) + j = validate(await request.get_json(), GUILD_UPDATE) + + # TODO: check MANAGE_GUILD + + if 'owner_id' in j: + await guild_owner_check(user_id, guild_id) + + await app.db.execute(""" + UPDATE guilds + SET owner_id = $1 + WHERE guild_id = $2 + """, int(j['owner_id']), guild_id) + + if 'name' in j: + await app.db.execute(""" + UPDATE guilds + SET name = $1 + WHERE guild_id = $2 + """, j['name'], guild_id) + + if 'region' in j: + # TODO: check region value + + await app.db.execute(""" + UPDATE guilds + SET region = $1 + WHERE guild_id = $2 + """, j['region'], guild_id) + + fields = ['verification_level', 'default_message_notifications', + 'explicit_content_filter', 'afk_timeout'] + + for field in [f for f in fields if f in j]: + await app.db.execute(""" + UPDATE guilds + SET {field} = $1 + WHERE guild_id = $2 + """, j[field], guild_id) + + channel_fields = ['afk_channel_id', 'system_channel_id'] + for field in [f for f in channel_fields if f in j]: + # TODO: check channel link to guild + + await app.db.execute(""" + UPDATE guilds + SET {field} = $1 + WHERE guild_id = $2 + """, j[field], guild_id) + + # return guild object + gj = await app.storage.get_guild(guild_id, user_id) + gj_extra = await app.storage.get_guild_extra(guild_id, user_id, 250) + + gj_total = {**gj, **gj_extra} + + await app.dispatcher.dispatch_guild(guild_id, 'GUILD_UPDATE', gj_total) + + return jsonify({**gj, **gj_extra}) + + @bp.route('/', methods=['DELETE']) async def delete_guild(guild_id): """Delete a guild.""" user_id = await token_check() - - owner_id = await app.db.fetchval(""" - SELECT owner_id - FROM guilds - WHERE guild_id = $1 - """, guild_id) - - if not owner_id: - raise GuildNotFound() - - if user_id != owner_id: - raise Forbidden('You are not the owner of the guild') + await guild_owner_check(user_id, guild_id) await app.db.execute(""" DELETE FROM guild @@ -188,6 +258,17 @@ async def create_channel(guild_id): return jsonify(channel) +@bp.route('//channels', methods=['PATCH']) +async def modify_channel_pos(guild_id): + user_id = await token_check() + await guild_check(user_id, guild_id) + await request.get_json() + + # TODO: this route + + raise NotImplementedError + + @bp.route('//members/', methods=['GET']) async def get_guild_member(guild_id, member_id): user_id = await token_check() @@ -221,6 +302,52 @@ async def get_members(guild_id): return jsonify(members) +@bp.route('//members/', methods=['PATCH']) +async def modify_guild_member(guild_id, member_id): + j = await request.get_json() + + if 'nick' in j: + # TODO: check MANAGE_NICKNAMES + + await app.db.execute(""" + UPDATE members + SET nickname = $1 + WHERE user_id = $2 AND guild_id = $3 + """, j['nick'], member_id, guild_id) + + if 'mute' in j: + # TODO: check MUTE_MEMBERS + + await app.db.execute(""" + UPDATE members + SET muted = $1 + WHERE user_id = $2 AND guild_id = $3 + """, j['mute'], member_id, guild_id) + + if 'deaf' in j: + # TODO: check DEAFEN_MEMBERS + + await app.db.execute(""" + UPDATE members + SET deafened = $1 + WHERE user_id = $2 AND guild_id = $3 + """, j['deaf'], member_id, guild_id) + + if 'channel_id' in j: + # TODO: check MOVE_MEMBERS + # TODO: change the member's voice channel + pass + + member = await app.storage.get_member_data_one(guild_id, member_id) + member.pop('joined_at') + + await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', {**{ + 'guild_id': str(guild_id) + }, **member}) + + return '', 204 + + @bp.route('//members/@me/nick', methods=['PATCH']) async def update_nickname(guild_id): user_id = await token_check() @@ -234,17 +361,11 @@ async def update_nickname(guild_id): WHERE user_id = $2 AND guild_id = $3 """, j['nick'], user_id, guild_id) - roles = await app.db.fetch(""" - SELECT role_id - FROM member_roles - WHERE user_id = $1 AND guild_id = $2 - """, user_id, guild_id) + member = await app.storage.get_member_data_one(guild_id, user_id) + member.pop('joined_at') - await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', { - 'guild_id': str(guild_id), - 'roles': list(map(str, roles)), - 'user': await app.storage.get_user(user_id), - 'nick': j['nick'], - }) + await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', {**{ + 'guild_id': str(guild_id) + }, **member}) return j['nick'] diff --git a/litecord/errors.py b/litecord/errors.py index 5e72376..7309607 100644 --- a/litecord/errors.py +++ b/litecord/errors.py @@ -5,6 +5,10 @@ class LitecordError(Exception): def message(self): return self.args[0] + @property + def json(self): + return self.args[1] + class BadRequest(LitecordError): status_code = 400 diff --git a/litecord/schemas.py b/litecord/schemas.py new file mode 100644 index 0000000..c66a5df --- /dev/null +++ b/litecord/schemas.py @@ -0,0 +1,63 @@ +import re + +from cerberus import Validator + +from .errors import BadRequest + +USERNAME_REGEX = re.compile(r'^[a-zA-Z0-9_]{2,19}$', re.A) +EMAIL_REGEX = re.compile(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', + re.A) + +class LitecordValidator(Validator): + def _validate_type_username(self, value: str) -> bool: + """Validate against the username regex.""" + return bool(USERNAME_REGEX.match(value)) + + +def validate(reqjson, schema): + validator = LitecordValidator(schema) + if not validator.validate(reqjson): + errs = validator.errors + + raise BadRequest('bad payload', errs) + + return reqjson + + +GUILD_UPDATE = { + 'name': { + 'type': 'string', + 'minlength': 2, + 'maxlength': 100, + 'required': False + }, + 'region': {'type': 'voice_region', 'required': False}, + 'icon': {'type': 'icon', 'required': False}, + + 'verification_level': {'type': 'verification_level', 'required': False}, + 'default_message_notifications': { + 'type': 'msg_notifications', + 'required': False, + }, + 'explicit_content_filter': {'type': 'explicit_content', 'required': False}, + + 'afk_channel_id': {'type': 'snowflake', 'required': False}, + 'afk_timeout': {'type': 'number', 'required': False}, + + 'owner_id': {'type': 'snowflake', 'required': False}, + + 'system_channel_id': {'type': 'snowflake', 'required': False}, +} + + +MEMBER_UPDATE = { + 'nick': { + 'type': 'nickname', + 'minlength': 1, 'maxlength': 100, + 'required': False, + }, + 'roles': {'type': 'list', 'required': False}, + 'mute': {'type': 'bool', 'required': False}, + 'deaf': {'type': 'bool', 'required': False}, + 'channel_id': {'type': 'snowflake', 'required': False}, +} diff --git a/run.py b/run.py index c35516a..339bf3c 100644 --- a/run.py +++ b/run.py @@ -76,12 +76,17 @@ async def app_after_serving(): @app.errorhandler(LitecordError) async def handle_litecord_err(err): - return jsonify({ + try: + ejson = err.json + except IndexError: + ejson = {} + + return jsonify({**{ 'error': True, # 'code': err.code, 'status': err.status_code, 'message': err.message, - }), err.status_code + }, **ejson}), err.status_code @app.errorhandler(500)