From 86705b0645b9449bcfdce5681b2d3f72efafdd04 Mon Sep 17 00:00:00 2001 From: Luna Mendes Date: Fri, 26 Oct 2018 21:03:44 -0300 Subject: [PATCH] blueprints.guilds: add 2 role endpoints Add PATCH /api/v6/guilds/:id/roles for multiple position changes for roles and PATCH /api/v6/guilds/:id/roles/:id for single guild role changes - permissions: add int maigc method - schemas: add ROLE_UPDATE and ROLE_UPDATE_POSITION --- litecord/blueprints/guilds.py | 153 +++++++++++++++++++++++++++++++++- litecord/permissions.py | 3 + litecord/schemas.py | 22 +++++ 3 files changed, 175 insertions(+), 3 deletions(-) diff --git a/litecord/blueprints/guilds.py b/litecord/blueprints/guilds.py index 5410551..90b359b 100644 --- a/litecord/blueprints/guilds.py +++ b/litecord/blueprints/guilds.py @@ -4,7 +4,10 @@ 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_CREATE, GUILD_UPDATE, ROLE_CREATE +from ..schemas import ( + validate, GUILD_CREATE, GUILD_UPDATE, ROLE_CREATE, ROLE_UPDATE, + ROLE_UPDATE_POSITION +) from ..utils import dict_get from .channels import channel_ack from .checks import guild_check @@ -88,8 +91,8 @@ async def create_role(guild_id, name: str, **kwargs): # set position = 0 when there isn't any # other role (when we're creating the # @everyone role) - max_pos + 1 if max_pos else 0, - dict_get(kwargs, 'permissions', default_perms), + max_pos + 1 if max_pos is not None else 0, + int(dict_get(kwargs, 'permissions', default_perms)), False, dict_get(kwargs, 'mentionable', False) ) @@ -397,6 +400,150 @@ async def create_guild_role(guild_id: int): return jsonify(role) +async def _role_update_dispatch(role_id: int, guild_id: int): + """Dispatch a GUILD_ROLE_UPDATE with updated information on a role.""" + role = await app.storage.get_role(role_id, guild_id) + + await app.dispatcher.dispatch_guild(guild_id, 'GUILD_ROLE_UPDATE', { + 'guild_id': str(guild_id), + 'role': role, + }) + + return role + + +async def _role_pairs_update(guild_id: int, pairs: list): + """Update the roles' 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 + + 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) + + await conn.execute(""" + UPDATE roles + SET position = $1 + WHERE roles.id = $2 + """, new_pos_2, role_2) + + 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) + + +@bp.route('//roles', methods=['PATCH']) +async def update_guild_role_positions(guild_id): + """Update the positions for a bunch of roles.""" + user_id = await token_check() + + # TODO: check MANAGE_ROLES + await guild_owner_check(user_id, guild_id) + + raw_j = await request.get_json() + + # we need to do this hackiness because thats + # cerberus for ya. + j = validate({'roles': raw_j}, ROLE_UPDATE_POSITION) + + # extract the list out + j = j['roles'] + print(j) + + all_roles = await app.storage.get_role_data(guild_id) + + # we'll have to calculate pairs of changing roles, + # then do the changes, etc. + roles_pos = {role['position']: int(role['id']) for role in all_roles} + new_positions = {role['id']: role['position'] for role in j} + + # always ignore people trying to change the @everyone role + # TODO: check if the user can even change the roles in the first place, + # preferrably when we have a proper perms system. + try: + new_positions.pop(guild_id) + except KeyError: + pass + + pairs = [] + + # we want to find pairs of (role_1, new_position_1) + # where new_position_1 is actually pointing to position_2 (for a role 2) + # AND we have (role_2, new_position_2) in the list of new_positions. + + # I hope the explanation went through. + + for change in j: + role_1, new_pos_1 = change['id'], change['position'] + + # check current pairs + # so we don't repeat a role + flag = False + + for pair in pairs: + if (role_1, new_pos_1) in pair: + flag = True + + # skip if found + if flag: + continue + + # find a role that is in that new position + role_2 = roles_pos.get(new_pos_1) + + # search role_2 in the new_positions list + new_pos_2 = new_positions.get(role_2) + + # if we found it, add it to the pairs array. + if new_pos_2: + pairs.append( + ((role_1, new_pos_1), (role_2, new_pos_2)) + ) + + await _role_pairs_update(guild_id, pairs) + + # return the list of all roles back + return jsonify(await app.storage.get_role_data(guild_id)) + + +@bp.route('//roles/', methods=['PATCH']) +async def update_guild_role(guild_id, role_id): + """Update a single role's information.""" + user_id = await token_check() + + # TODO: check MANAGE_ROLES + await guild_owner_check(user_id, guild_id) + + j = validate(await request.get_json(), ROLE_UPDATE) + + # we only update ints on the db, not Permissions + j['permissions'] = int(j['permissions']) + + for field in j: + await app.db.execute(f""" + UPDATE roles + SET {field} = $1 + WHERE roles.id = $2 AND roles.guild_id = $3 + """, j[field], role_id, guild_id) + + role = await _role_update_dispatch(role_id, guild_id) + return jsonify(role) + + @bp.route('//members/', methods=['GET']) async def get_guild_member(guild_id, member_id): """Get a member's information in a guild.""" diff --git a/litecord/permissions.py b/litecord/permissions.py index 1f7a020..c5c5966 100644 --- a/litecord/permissions.py +++ b/litecord/permissions.py @@ -50,5 +50,8 @@ class Permissions(ctypes.Union): def __init__(self, val: int): self.binary = val + def __int__(self): + return self.binary + def numby(self): return self.binary diff --git a/litecord/schemas.py b/litecord/schemas.py index 8eb4bfc..77147ab 100644 --- a/litecord/schemas.py +++ b/litecord/schemas.py @@ -259,6 +259,28 @@ ROLE_CREATE = { 'mentionable': {'type': 'boolean', 'default': False}, } +ROLE_UPDATE = { + 'name': {'type': 'string', 'required': False}, + 'permissions': {'coerce': Permissions, 'required': False}, + 'color': {'coerce': Color, 'required': False}, + 'hoist': {'type': 'boolean', 'required': False}, + 'mentionable': {'type': 'boolean', 'required': False}, +} + + +ROLE_UPDATE_POSITION = { + 'roles': { + 'type': 'list', + 'schema': { + 'type': 'dict', + 'schema': { + 'id': {'coerce': int}, + 'position': {'coerce': int}, + }, + } + } +} + MEMBER_UPDATE = { 'nick': {