make other blueprints use common, etc

This commit is contained in:
Luna 2019-10-25 13:33:52 -03:00
parent 1efc65511c
commit a67b6580ba
11 changed files with 78 additions and 253 deletions

View File

@ -22,7 +22,7 @@ from quart import Blueprint, jsonify, current_app as app, request
from litecord.auth import admin_check
from litecord.schemas import validate
from litecord.admin_schemas import GUILD_UPDATE
from litecord.blueprints.guilds import delete_guild
from litecord.common.guilds import delete_guild
from litecord.errors import GuildNotFound
bp = Blueprint("guilds_admin", __name__)

View File

@ -23,10 +23,9 @@ from quart import Blueprint, request, current_app as app, jsonify
from logbook import Logger
from litecord.utils import async_map
from litecord.utils import async_map, query_tuple_from_args, extract_limit
from litecord.blueprints.auth import token_check
from litecord.blueprints.checks import channel_check, channel_perm_check
from litecord.blueprints.channel.messages import query_tuple_from_args, extract_limit
from litecord.enums import GUILD_CHANS
@ -165,7 +164,8 @@ def _emoji_sql_simple(emoji: str, param=4):
return emoji_sql(emoji_type, emoji_id, emoji_name, param)
async def remove_reaction(channel_id: int, message_id: int, user_id: int, emoji: str):
async def _remove_reaction(channel_id: int, message_id: int, user_id: int, emoji: str):
"""Remove given reaction from a message."""
ctype, guild_id = await channel_check(user_id, channel_id)
emoji_type, emoji_id, emoji_name = emoji_info_from_str(emoji)
@ -201,8 +201,7 @@ async def remove_own_reaction(channel_id, message_id, emoji):
"""Remove a reaction."""
user_id = await token_check()
await remove_reaction(channel_id, message_id, user_id, emoji)
await _remove_reaction(channel_id, message_id, user_id, emoji)
return "", 204
@ -212,7 +211,7 @@ async def remove_user_reaction(channel_id, message_id, emoji, other_id):
user_id = await token_check()
await channel_perm_check(user_id, channel_id, "manage_messages")
await remove_reaction(channel_id, message_id, other_id, emoji)
await _remove_reaction(channel_id, message_id, other_id, emoji)
return "", 204

View File

@ -42,6 +42,7 @@ from litecord.blueprints.dm_channels import gdm_remove_recipient, gdm_destroy
from litecord.utils import search_result_from_list
from litecord.embed.messages import process_url_embed, msg_update_embeds
from litecord.snowflake import snowflake_datetime
from litecord.common.channels import channel_ack
log = Logger(__name__)
bp = Blueprint("channels", __name__)
@ -136,7 +137,7 @@ async def _update_guild_chan_cat(guild_id: int, channel_id: int):
await app.dispatcher.dispatch_guild(guild_id, "CHANNEL_UPDATE", child)
async def delete_messages(channel_id):
async def _delete_messages(channel_id):
await app.db.execute(
"""
DELETE FROM channel_pins
@ -162,7 +163,7 @@ async def delete_messages(channel_id):
)
async def guild_cleanup(channel_id):
async def _guild_cleanup(channel_id):
await app.db.execute(
"""
DELETE FROM channel_overwrites
@ -220,8 +221,8 @@ async def close_channel(channel_id):
# didn't work on my setup, so I delete
# everything before moving to the main
# channel table deletes
await delete_messages(channel_id)
await guild_cleanup(channel_id)
await _delete_messages(channel_id)
await _guild_cleanup(channel_id)
await app.db.execute(
f"""
@ -595,48 +596,6 @@ async def trigger_typing(channel_id):
return "", 204
async def channel_ack(user_id, guild_id, channel_id, message_id: int = None):
"""ACK a channel."""
if not message_id:
message_id = await app.storage.chan_last_message(channel_id)
await app.db.execute(
"""
INSERT INTO user_read_state
(user_id, channel_id, last_message_id, mention_count)
VALUES
($1, $2, $3, 0)
ON CONFLICT ON CONSTRAINT user_read_state_pkey
DO
UPDATE
SET last_message_id = $3, mention_count = 0
WHERE user_read_state.user_id = $1
AND user_read_state.channel_id = $2
""",
user_id,
channel_id,
message_id,
)
if guild_id:
await app.dispatcher.dispatch_user_guild(
user_id,
guild_id,
"MESSAGE_ACK",
{"message_id": str(message_id), "channel_id": str(channel_id)},
)
else:
# we don't use ChannelDispatcher here because since
# guild_id is None, all user devices are already subscribed
# to the given channel (a dm or a group dm)
await app.dispatcher.dispatch_user(
user_id,
"MESSAGE_ACK",
{"message_id": str(message_id), "channel_id": str(channel_id)},
)
@bp.route("/<int:channel_id>/messages/<int:message_id>/ack", methods=["POST"])
async def ack_channel(channel_id, message_id):
"""Acknowledge a channel."""

View File

@ -31,6 +31,7 @@ from ..snowflake import get_snowflake
from .auth import token_check
from litecord.blueprints.dm_channels import gdm_create, gdm_add_recipient
from litecord.common.channels import try_dm_state
log = Logger(__name__)
bp = Blueprint("dms", __name__)
@ -44,24 +45,6 @@ async def get_dms():
return jsonify(dms)
async def try_dm_state(user_id: int, dm_id: int):
"""Try inserting the user into the dm state
for the given DM.
Does not do anything if the user is already
in the dm state.
"""
await app.db.execute(
"""
INSERT INTO dm_channel_state (user_id, dm_id)
VALUES ($1, $2)
ON CONFLICT DO NOTHING
""",
user_id,
dm_id,
)
async def jsonify_dm(dm_id: int, user_id: int):
dm_chan = await app.storage.get_dm(dm_id, user_id)
return jsonify(dm_chan)

View File

@ -27,77 +27,11 @@ 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.common.guilds import create_guild_channel
bp = Blueprint("guild_channels", __name__)
async def _specific_chan_create(channel_id, ctype, **kwargs):
if ctype == ChannelType.GUILD_TEXT:
await app.db.execute(
"""
INSERT INTO guild_text_channels (id, topic)
VALUES ($1, $2)
""",
channel_id,
kwargs.get("topic", ""),
)
elif ctype == ChannelType.GUILD_VOICE:
await app.db.execute(
"""
INSERT INTO guild_voice_channels (id, bitrate, user_limit)
VALUES ($1, $2, $3)
""",
channel_id,
kwargs.get("bitrate", 64),
kwargs.get("user_limit", 0),
)
async def create_guild_channel(
guild_id: int, channel_id: int, ctype: ChannelType, **kwargs
):
"""Create a channel in a guild."""
await app.db.execute(
"""
INSERT INTO channels (id, channel_type)
VALUES ($1, $2)
""",
channel_id,
ctype.value,
)
# calc new pos
max_pos = await app.db.fetchval(
"""
SELECT MAX(position)
FROM guild_channels
WHERE guild_id = $1
""",
guild_id,
)
# account for the first channel in a guild too
max_pos = max_pos or 0
# 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,
)
# the rest of sql magic is dependant on the channel
# we're creating (a text or voice or category),
# so we use this function.
await _specific_chan_create(channel_id, ctype, **kwargs)
@bp.route("/<int:guild_id>/channels", methods=["GET"])
async def get_guild_channels(guild_id):
"""Get the list of channels in a guild."""

View File

@ -23,47 +23,11 @@ from litecord.blueprints.auth import token_check
from litecord.blueprints.checks import guild_perm_check
from litecord.schemas import validate, GUILD_PRUNE
from litecord.common.guilds import remove_member, remove_member_multi
bp = Blueprint("guild_moderation", __name__)
async def remove_member(guild_id: int, member_id: int):
"""Do common tasks related to deleting a member from the guild,
such as dispatching GUILD_DELETE and GUILD_MEMBER_REMOVE."""
await app.db.execute(
"""
DELETE FROM members
WHERE guild_id = $1 AND user_id = $2
""",
guild_id,
member_id,
)
await app.dispatcher.dispatch_user_guild(
member_id,
guild_id,
"GUILD_DELETE",
{"guild_id": str(guild_id), "unavailable": False},
)
await app.dispatcher.unsub("guild", guild_id, member_id)
await app.dispatcher.dispatch("lazy_guild", guild_id, "remove_member", member_id)
await app.dispatcher.dispatch_guild(
guild_id,
"GUILD_MEMBER_REMOVE",
{"guild_id": str(guild_id), "user": await app.storage.get_user(member_id)},
)
async def remove_member_multi(guild_id: int, members: list):
"""Remove multiple members."""
for member_id in members:
await remove_member(guild_id, member_id)
@bp.route("/<int:guild_id>/members/<int:member_id>", methods=["DELETE"])
async def kick_guild_member(guild_id, member_id):
"""Remove a member from a guild."""
@ -221,6 +185,5 @@ async def begin_guild_prune(guild_id):
days = j["days"]
member_ids = await get_prune(guild_id, days)
app.loop.create_task(remove_member_multi(guild_id, member_ids))
app.sched.spawn(remove_member_multi(guild_id, member_ids))
return jsonify({"pruned": len(member_ids)})

View File

@ -27,11 +27,9 @@ from litecord.auth import token_check
from litecord.blueprints.checks import guild_check, guild_perm_check
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
from litecord.utils import maybe_lazy_guild_dispatch
from litecord.common.guilds import create_role
DEFAULT_EVERYONE_PERMS = 104324161
log = Logger(__name__)
bp = Blueprint("guild_roles", __name__)
@ -45,71 +43,6 @@ async def get_guild_roles(guild_id):
return jsonify(await app.storage.get_role_data(guild_id))
async def _maybe_lg(guild_id: int, event: str, role, force: bool = False):
# sometimes we want to dispatch an event
# even if the role isn't hoisted
# an example of such a case is when a role loses
# its hoist status.
# check if is a dict first because role_delete
# only receives the role id.
if isinstance(role, dict) and not role["hoist"] and not force:
return
await app.dispatcher.dispatch("lazy_guild", guild_id, event, role)
async def create_role(guild_id, name: str, **kwargs):
"""Create a role in a guild."""
new_role_id = get_snowflake()
everyone_perms = await get_role_perms(guild_id, guild_id)
default_perms = dict_get(kwargs, "default_perms", everyone_perms.binary)
# update all roles so that we have space for pos 1, but without
# sending GUILD_ROLE_UPDATE for everyone
await app.db.execute(
"""
UPDATE roles
SET
position = position + 1
WHERE guild_id = $1
AND NOT (position = 0)
""",
guild_id,
)
await app.db.execute(
"""
INSERT INTO roles (id, guild_id, name, color,
hoist, position, permissions, managed, mentionable)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
""",
new_role_id,
guild_id,
name,
dict_get(kwargs, "color", 0),
dict_get(kwargs, "hoist", False),
# always set ourselves on position 1
1,
int(dict_get(kwargs, "permissions", default_perms)),
False,
dict_get(kwargs, "mentionable", False),
)
role = await app.storage.get_role(new_role_id, guild_id)
# we need to update the lazy guild handlers for the newly created group
await _maybe_lg(guild_id, "new_role", role)
await app.dispatcher.dispatch_guild(
guild_id, "GUILD_ROLE_CREATE", {"guild_id": str(guild_id), "role": role}
)
return role
@bp.route("/<int:guild_id>/roles", methods=["POST"])
async def create_guild_role(guild_id: int):
"""Add a role to a guild"""
@ -132,7 +65,7 @@ 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 _maybe_lg(guild_id, "role_pos_upd", role)
await maybe_lazy_guild_dispatch(guild_id, "role_pos_upd", role)
await app.dispatcher.dispatch_guild(
guild_id, "GUILD_ROLE_UPDATE", {"guild_id": str(guild_id), "role": role}
@ -343,7 +276,7 @@ async def update_guild_role(guild_id, role_id):
)
role = await _role_update_dispatch(role_id, guild_id)
await _maybe_lg(guild_id, "role_update", role, True)
await maybe_lazy_guild_dispatch(guild_id, "role_update", role, True)
return jsonify(role)
@ -369,7 +302,7 @@ async def delete_guild_role(guild_id, role_id):
if res == "DELETE 0":
return "", 204
await _maybe_lg(guild_id, "role_delete", role_id, True)
await maybe_lazy_guild_dispatch(guild_id, "role_delete", role_id, True)
await app.dispatcher.dispatch_guild(
guild_id,

View File

@ -28,7 +28,7 @@ from ..schemas import validate, USER_UPDATE, GET_MENTIONS
from .guilds import guild_check
from litecord.auth import token_check, hash_data, check_username_usage, roll_discrim
from litecord.blueprints.guild.mod import remove_member
from litecord.common.guilds import remove_member
from litecord.enums import PremiumType
from litecord.images import parse_data_uri
@ -319,7 +319,6 @@ async def leave_guild(guild_id: int):
await guild_check(user_id, guild_id)
await remove_member(guild_id, user_id)
return "", 204

View File

@ -43,7 +43,7 @@ from litecord.snowflake import get_snowflake
from litecord.utils import async_map
from litecord.errors import WebhookNotFound, Unauthorized, ChannelNotFound, BadRequest
from litecord.blueprints.channel.messages import (
from litecord.common.messages import (
msg_create_request,
msg_create_check_content,
msg_add_attachment,

View File

@ -116,6 +116,10 @@ class Forbidden(LitecordError):
status_code = 403
class ForbiddenDM(Forbidden):
error_code = 50007
class NotFound(LitecordError):
status_code = 404

View File

@ -23,7 +23,9 @@ from typing import Any, Iterable, Optional, Sequence, List, Dict, Union
from logbook import Logger
from quart.json import JSONEncoder
from quart import current_app as app
from quart import current_app as app, request
from .errors import BadRequest
log = Logger(__name__)
@ -233,3 +235,52 @@ def maybe_int(val: Any) -> Union[int, Any]:
return int(val)
except (ValueError, TypeError):
return val
async def maybe_lazy_guild_dispatch(
guild_id: int, event: str, role, force: bool = False
):
# sometimes we want to dispatch an event
# even if the role isn't hoisted
# an example of such a case is when a role loses
# its hoist status.
# check if is a dict first because role_delete
# only receives the role id.
if isinstance(role, dict) and not role["hoist"] and not force:
return
await app.dispatcher.dispatch("lazy_guild", guild_id, event, role)
def extract_limit(request_, default: int = 50, max_val: int = 100):
"""Extract a limit kwarg."""
try:
limit = int(request_.args.get("limit", default))
if limit not in range(0, max_val + 1):
raise ValueError()
except (TypeError, ValueError):
raise BadRequest("limit not int")
return limit
def query_tuple_from_args(args: dict, limit: int) -> tuple:
"""Extract a 2-tuple out of request arguments."""
before, after = None, None
if "around" in request.args:
average = int(limit / 2)
around = int(args["around"])
after = around - average
before = around + average
elif "before" in args:
before = int(args["before"])
elif "after" in args:
before = int(args["after"])
return before, after