def channel_invite(token, channel_id, u_id):
    """Invites a user (with user id u_id) to join a channel with ID channel_id.
    Once invited the user is added to the channel immediately"""
    # Convert channel_id and u_id to integer
    channel_id = int(channel_id)
    u_id = int(u_id)

    # Validate token, channel and user
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_channel(channel_id):
        raise ValueError(INVALID_CHANNEL)
    if not validator.is_valid_user(u_id):
        raise ValueError(INVALID_USER)

    # Find invitor
    invitor_id = jwt_handler.decode_token(token)

    # Locate channel in database
    channel = database.get_channel_by_id(channel_id)

    # Check that the invitor is authorised
    auth_list = channel["auth_ids"]

    if invitor_id not in auth_list:
        raise AccessError(CHANNEL_INV_NO_AUTH)
    if u_id in auth_list:
        raise ValueError(CHANNEL_ALREADY_JOINED)
    auth_list.append(u_id)
    database.update_channel_by_id(channel_id, {
        "auth_ids": auth_list
    })
    return {}
def channel_removeowner(token, channel_id, u_id):
    """Remove user with user id u_id an owner of this channel"""
    # Convert channel_id and u_id to integer
    channel_id = int(channel_id)
    u_id = int(u_id)

    # Validate token, channel and user
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_channel(channel_id):
        raise ValueError(INVALID_CHANNEL)
    if not validator.is_valid_user(u_id):
        raise ValueError(INVALID_USER)

    # Locate channel and user in database
    channel = database.get_channel_by_id(channel_id)
    auth_user_id = jwt_handler.decode_token(token)
    auth_user = database.get_user_by_id(auth_user_id)
    owner_list = channel["owner_ids"]

    if auth_user["permission"] not in [OWNER, ADMIN] or\
            auth_user_id not in channel["owner_ids"]:
        raise AccessError(CHANNEL_DEL_OWNER_NO_AUTH)

    # Check that the added_user is currently an owner
    if u_id not in owner_list:
        raise ValueError(CHANNEL_NOT_OWNER)

    # Remove user from owner_ids
    owner_list.remove(u_id)
    database.update_channel_by_id(channel_id, {
        "owner_ids": owner_list
    })

    return {}
def message_remove(token, message_id):
    """
    Given a message_id for a message, this message is removed from the chxnel
    """
    # Validate token
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)

    if not isinstance(message_id, int):
        message_id = int(message_id)

    # Convert token to u_id
    u_id = jwt_handler.decode_token(token)

    # Check u_id is authorised
    user = database.get_user_by_id(u_id)
    message = database.get_message_by_id(message_id)
    if message is None:
        raise ValueError(MESSAGE_REMOVE_NOT_EXIST)

    channel_id = message["channel_id"]
    channel = database.get_channel_by_id(channel_id)
    # if sender of message
    if u_id == message["u_id"] or\
            user["permission"] in [OWNER, ADMIN] or\
            u_id in channel["owner_ids"]:
        # Delete message from database
        database.delete_message_by_id(message_id)
        # Delete message from channel
        channel["messages"].remove(message_id)
        database.update_channel_by_id(channel_id, channel)
        return {}
    raise AccessError(MESSAGE_EDIT_NO_AUTH)
def message_pin(token, message_id):
    """
    Given a message within a channel, mark it as "pinned" to be given special
    display treatment by the frontend
    """
    # Validate token
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)

    # Decode token
    u_id = jwt_handler.decode_token(token)

    user = database.get_user_by_id(u_id)
    message = database.get_message_by_id(message_id)
    if message is None:
        raise ValueError(MESSAGE_PIN_NOT_EXIST)
    channel = database.get_channel_by_id(message["channel_id"])

    if user["permission"] not in [OWNER, ADMIN] \
            and u_id not in channel["owner_ids"]:
        raise ValueError(MESSAGE_PIN_NO_AUTH)

    # Checking data
    if u_id not in channel["auth_ids"]:
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    if message["is_pinned"] is True:
        raise ValueError(MESSAGE_ALREADY_PINNED)

    # Pinning message
    database.update_message_by_id(message_id, {"is_pinned": True})
    return {}
def message_unpin(token, message_id):
    """Given a message within a channel, remove it's mark as unpinned"""
    # Validate token
    if not validator.is_valid_token(token):
        raise ValueError(INVALID_TOKEN)

    # Decode token
    u_id = jwt_handler.decode_token(token)

    user = database.get_user_by_id(u_id)
    message = database.get_message_by_id(message_id)
    if message is None:
        raise ValueError(MESSAGE_UNPIN_NOT_EXIST)
    channel = database.get_channel_by_id(message["channel_id"])

    if user["permission"] not in [OWNER, ADMIN] \
            and u_id not in channel["owner_ids"]:
        raise AccessError(MESSAGE_UNPIN_NO_AUTH)

    # Checking data
    if u_id not in channel["auth_ids"]:
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    if message["is_pinned"] is False:
        raise ValueError(MESSAGE_NOT_PINNED)

    # Unpinning message
    database.update_message_by_id(message_id, {"is_pinned": False})
    return {}
def channel_join(token, channel_id):
    """Given a channel_id of a channel that the authorised user can join, adds
    them to that channel"""
    # Validate token and channel ID
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_channel(channel_id):
        raise ValueError(INVALID_CHANNEL)

    # Convert token to u_id
    u_id = jwt_handler.decode_token(token)
    user = database.get_user_by_id(u_id)

    # Locate channel in database
    channel = database.get_channel_by_id(channel_id)

    # If channel is private
    if not channel["is_public"] and user["permission"] == MEMBER:
        # If the user is not an admin/owner
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    # Add user to authorised users in channel
    auth_list = channel["auth_ids"]
    if u_id not in auth_list:
        auth_list.append(u_id)
        database.update_channel_by_id(channel_id, {
            "auth_ids": auth_list
        })
    else:
        raise ValueError(CHANNEL_ALREADY_JOINED)

    return {}
def channel_leave(token, channel_id):
    """Given a channel ID, the user removed as a member of this channel"""
    # Validate token and channel ID
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_channel(channel_id):
        raise ValueError(INVALID_CHANNEL)

    # Convert token to u_id
    u_id = jwt_handler.decode_token(token)


    # Locate channel
    channel = database.get_channel_by_id(channel_id)

    # Validate user is in channel
    auth_list = channel["auth_ids"]
    if u_id not in auth_list:
        raise AccessError(NOT_IN_CHANNEL)

    # Remove user from auth_list
    auth_list.remove(u_id)
    database.update_channel_by_id(channel_id, {
        "auth_ids": auth_list
    })

    # If owner, remove from owner_list
    owner_list = channel["owner_ids"]
    if u_id in owner_list:
        owner_list.remove(u_id)
        database.update_channel_by_id(channel_id, {
            "owner_ids": owner_list
        })

    return {}
def message_edit(token, message_id, message):
    """Given a message, update it's text with new text"""
    # Validate token
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)

    # If the message is empty, perform deletion instead
    if message == "":
        return message_remove(token, message_id)

    if not validator.is_valid_message(message):
        raise ValueError(INVALID_MESSAGE)

    # Convert token to u_id
    u_id = jwt_handler.decode_token(token)

    user = database.get_user_by_id(u_id)
    message_dict = database.get_message_by_id(message_id)
    if message_dict is None:
        raise ValueError(MESSAGE_EDIT_NOT_EXIST)

    channel_id = message_dict["channel_id"]
    channel = database.get_channel_by_id(channel_id)

    # Check if user can edit this message
    if u_id == message_dict["u_id"] or user["permission"] in [OWNER, ADMIN] \
            or u_id in channel["owner_ids"]:
        database.update_message_by_id(message_id, {"message": message})
        return {}
    raise AccessError(MESSAGE_EDIT_NO_AUTH)
def message_sendlater(token, channel_id, message, time_sent):
    """
    Send a message from authorised_user to the channel specified by channel_id
    """
    # Validate data
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_message(message):
        raise ValueError(INVALID_MESSAGE)

    # Convert token to u_id
    u_id = jwt_handler.decode_token(token)
    # Check channel exists
    channel = database.get_channel_by_id(channel_id)
    if channel is None:
        raise ValueError(INVALID_CHANNEL)

    # Read time_sent
    time_to_send = json_time_translator.timestamp_to_datetime(time_sent)
    # Check time_to_send is not in the past
    if time_to_send <= datetime.utcnow():
        raise ValueError(MESSAGE_TIME_INVALID)

    if u_id not in channel["auth_ids"]:
        raise AccessError(MESSAGE_SEND_NO_AUTH)

    # Create message
    message_id = database.add_message({
        "u_id":
        u_id,
        "message":
        message,
        "channel_id":
        int(channel_id),
        "time_created":
        json_time_translator.datetime_to_json(time_to_send),
        "is_pinned":
        False,
        "reacts": []
    })
    # Insert into channel's list of messages in order of time
    message_id_list = channel["messages"]

    inserted = False
    for i in reversed(range(len(message_id_list))):
        json_time = database.get_message_by_id(
            message_id_list[i])["time_created"]
        timestamp = json_time_translator.json_to_timestamp(json_time)
        if timestamp <= int(float(time_sent)):
            message_id_list.insert(i + 1, message_id)
            inserted = True
            break
    if inserted is False:
        message_id_list.insert(0, message_id)
    database.update_channel_by_id(channel_id, {"messages": message_id_list})
    return {"message_id": message_id}
def message_react(token, message_id, react_id):
    """
    Given a message within a channel the authorised user is part of, add a
    "react" to that particular message
    """
    # Validate
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)

    # Decode token
    u_id = jwt_handler.decode_token(token)

    # Change react_id to an integer
    if not isinstance(react_id, int):
        react_id = int(react_id)
    if not isinstance(message_id, int):
        message_id = int(message_id)

    # Validate data
    message = database.get_message_by_id(message_id)
    channel = database.get_channel_by_id(message["channel_id"])

    if u_id not in channel["auth_ids"]:
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    if react_id != 1:
        print(react_id)
        raise ValueError(INVALID_REACT)

    react_id_exists = False
    for react in message["reacts"]:
        if react_id == react["react_id"]:
            react_id_exists = True
            if u_id in react["u_id"]:
                raise ValueError(MESSAGE_ALREADY_REACTED)
            break

    # Add react to message
    react_list = message["reacts"]
    if not react_id_exists:
        react_list.append({"react_id": react_id, "u_id": [u_id]})
    else:
        for react in react_list:
            if react_id == react["react_id"]:
                react["u_id"].append(u_id)
                break

    database.update_message_by_id(message_id, {"reacts": react_list})
    return {}
def standup_start(token, channel_id, length):
    """Will return standup finish time if user and channel are both valid"""
    # Check valid token
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)

    length = int(length)

    # Check channel existence
    channel = database.get_channel_by_id(channel_id)
    if channel is None:
        raise ValueError(INVALID_CHANNEL)

    # Check user is member of channel
    u_id = jwt_handler.decode_token(token)
    if u_id not in channel["auth_ids"]:
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    # Check the startup is not currently active in this channel
    old_time_end = database.get_standup_finish_time(channel_id)
    if old_time_end is not None and\
            json_time_translator.json_to_datetime(old_time_end) >= datetime.utcnow():
        raise ValueError(STANDUP_RUNNING)

    # Check for non-zero length
    if length <= 0:
        raise ValueError(STANDUP_TIME_INVALID)

    # Set channel standup_end to now+X min and add finish time to database
    time_end_datetime = datetime.utcnow() + timedelta(seconds=length)
    time_end_json = json_time_translator.datetime_to_json(time_end_datetime)

    # Move old standup messages
    database.move_standup_to_unused_by_id(channel_id)

    database.update_standup_by_id(channel_id, {
        "time_finish": time_end_json,
        "length": length
    })

    time_end_timestamp = int(
        json_time_translator.datetime_to_timestamp(time_end_datetime))

    # Start threaded timer
    standup_timer_start(token, channel_id, length)

    return {"time_finish": time_end_timestamp}
def message_unreact(token, message_id, react_id):
    """
    Given a message within a channel the authorised user is part of, remove a
    "react" to that particular message
    """
    # Validate
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    # Decode token
    u_id = jwt_handler.decode_token(token)

    # Change react_id to an integer
    if not isinstance(react_id, int):
        react_id = int(react_id)
    if not isinstance(message_id, int):
        message_id = int(message_id)

    # Validate data
    message = database.get_message_by_id(message_id)
    channel = database.get_channel_by_id(message["channel_id"])

    if u_id not in channel["auth_ids"]:
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    if react_id != 1:
        print(react_id)
        raise ValueError(INVALID_REACT)

    react_list = message["reacts"]
    react_found = False
    for react in react_list:
        if react_id == react["react_id"] and u_id in react["u_id"]:
            react_found = True
            break

    if not react_found:
        raise ValueError(MESSAGE_NOT_REACTED)

    # Remove react from message
    for react in react_list:
        if react_id == react["react_id"]:
            react["u_id"].remove(u_id)

    database.update_message_by_id(message_id, {"reacts": react_list})
    return {}
def channel_details(token, channel_id):
    """Given a Channel with ID channel_id that the authorised user is part of,
    provide basic details about the channel"""
    # Validate token and channel
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_channel(channel_id):
        raise ValueError(INVALID_CHANNEL)

    # Find user in database
    u_id = jwt_handler.decode_token(token)

    # Locate in database
    channel = database.get_channel_by_id(channel_id)
    user = database.get_user_by_id(u_id)

    # Check user is authorised
    if u_id not in channel["auth_ids"] and user["permission"] == MEMBER:
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    # Compile channel details
    owner_list = channel["owner_ids"]
    auth_list = channel["auth_ids"]
    owner_list_with_details = []
    auth_list_with_details = []
    for owner_id in owner_list:
        owner = database.get_user_by_id(owner_id)
        owner_list_with_details.append({
            "u_id": owner_id,
            "name_first": owner["name_first"],
            "name_last": owner["name_last"],
            "profile_img_url": owner["profile_img_url"]})
    for auth_id in auth_list:
        auth = database.get_user_by_id(auth_id)
        auth_list_with_details.append({
            "u_id": auth_id,
            "name_first": auth["name_first"],
            "name_last": auth["name_last"],
            "profile_img_url": auth["profile_img_url"]})
    return {
        "name": channel["name"],
        "owner_members": owner_list_with_details,
        "all_members": auth_list_with_details
    }
def channel_addowner(token, channel_id, u_id):
    """Make user with user id u_id an owner of this channel"""
    # Convert channel_id and u_id to integer
    channel_id = int(channel_id)
    u_id = int(u_id)

    # Validate token, channel and user
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_channel(channel_id):
        raise ValueError(INVALID_CHANNEL)
    if not validator.is_valid_user(u_id):
        raise ValueError(INVALID_USER)

    # Locate channel and user in database
    channel = database.get_channel_by_id(channel_id)
    auth_user_id = jwt_handler.decode_token(token)
    auth_user = database.get_user_by_id(auth_user_id)

    # Check auth_user permissions
    if auth_user["permission"] not in [OWNER, ADMIN] or\
            auth_user_id not in channel["owner_ids"]:
        raise AccessError(CHANNEL_ADD_OWNER_NO_AUTH)


    # Add user to auth_ids
    auth_list = channel["auth_ids"]
    if u_id not in auth_list:
        auth_list.append(u_id)
        database.update_channel_by_id(channel_id, {
            "auth_ids": auth_list
        })

    # Add user to owner_ids
    owner_list = channel["owner_ids"]
    if u_id in owner_list:
        raise ValueError(CHANNEL_ALREADY_OWNER)
    owner_list.append(u_id)
    database.update_channel_by_id(channel_id, {
        "owner_ids": owner_list
    })

    return {}
def standup_active(token, channel_id):
    """Adds message to standup buffer if standup is currently active"""

    # Check valid token
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)

    # Check channel existence
    channel = database.get_channel_by_id(channel_id)
    if channel is None:
        raise ValueError(INVALID_CHANNEL)

    is_active = is_standup_running(channel_id)
    time_finish = None
    if is_active:
        time_finish = int(
            json_time_translator.json_to_timestamp(
                database.get_standup_finish_time(channel_id)))

    return {"is_active": is_active, "time_finish": time_finish}
def standup_send(token, channel_id, message):
    """Adds message to standup buffer if standup is currently active"""

    # Check valid token
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)

    # Check channel existence
    channel = database.get_channel_by_id(channel_id)
    if channel is None:
        raise ValueError(INVALID_CHANNEL)

    # Check user is member of channel
    u_id = jwt_handler.decode_token(token)
    if u_id not in channel["auth_ids"]:
        raise AccessError(CHANNEL_CANT_VIEW_DETAIL)

    # Check the startup is not currently active in this channel
    old_time_end = database.get_standup_finish_time(channel_id)
    if old_time_end is None or\
            json_time_translator.json_to_datetime(old_time_end) < datetime.utcnow():
        raise ValueError(STANDUP_NOT_RUNNING)

    # Check valid message
    if not validator.is_valid_message(message):
        raise ValueError(INVALID_MESSAGE)

    # Save message
    database.add_standup_message(
        channel_id, {
            "u_id":
            u_id,
            "message":
            message,
            "time_created":
            json_time_translator.datetime_to_json(datetime.utcnow()),
        })

    return {}
Exemple #17
0
def is_valid_channel(channel_id):
    """Returns whether or not a channel is valid"""
    if database.get_channel_by_id(channel_id) is None:
        return False
    return True
def channel_messages(token, channel_id, start):
    """Given a Channel with ID channel_id that the authorised user is part of,
    return up to 50 messages between index "start" and "start + 50". Message
    with index 0 is the most recent message in the channel. This function
    returns a new index "end" which is the value of "start + 50", or, if this
    function has returned the least recent messages in the channel, returns -1
    in "end" to indicate there are no more messages to load after this
    return."""
    # Convert 'start' to an integer
    start = int(start)

    # Validate token and channel ID
    if not validator.is_valid_token(token):
        raise AccessError(INVALID_TOKEN)
    if not validator.is_valid_channel(channel_id):
        raise ValueError(INVALID_CHANNEL)

    # Locate channel
    channel = database.get_channel_by_id(channel_id)

    # Validate that token user is in channel
    u_id = jwt_handler.decode_token(token)
    if u_id not in channel["auth_ids"]:
        raise AccessError(CHANNEL_CANT_VIEW_MSG)

    # Validate messages exist
    total_messages = len(channel['messages'])
    if total_messages <= 0:
        return {
            "messages": [],
            "start": start,
            "end": -1
        }

    # Validate start point
    if start > (total_messages - 1) or start < 0:
        raise ValueError(CHANNEL_NO_MORE_MSG)

    # Find index for the most recent message (reference list backwards)
    start_index = len(channel['messages']) - 1 - start
    # Get all recent messages up to 50
    messages = []
    end = start + 49
    for msg_num in range(50):
        index = start_index - msg_num
        # If there are less than 50 msgs
        if index < 0:
            end = -1
            break

        # Get message object
        message_id = channel['messages'][index]
        message = database.get_message_by_id(message_id)

        # If message is sent later or not there
        if message is None or\
                json_time_translator.json_to_datetime(message["time_created"]) >= datetime.utcnow():
            continue

        # Create output-able list of reacts
        react_list = []
        reacted_ids = []
        all_reacts = database.get_all_reacts(message_id)
        react_id = 1
        for react in all_reacts:
            is_this_user_reacted = False
            if react["react_id"] == react_id:
                reacted_ids.extend(react["u_id"])
                if u_id in react["u_id"]:
                    is_this_user_reacted = True
                # Only add the reaction if it has at least one count of a user
                react_list.append({
                    "react_id": react_id,
                    "u_ids": reacted_ids,
                    "is_this_user_reacted": is_this_user_reacted
                })

        # print(message["time_created"])
        # print(json_time_translator.json_to_datetime(message["time_created"]))
        # print(json_time_translator.json_to_timestamp(message["time_created"]))
        # Append to file
        messages.append({
            "message_id": message_id,
            "u_id": message["u_id"],
            "message": message["message"],
            "time_created": json_time_translator.json_to_timestamp(message["time_created"]),
            "reacts": react_list,
            "is_pinned": message["is_pinned"]
        })

    return {
        "messages": messages,
        "start": start,
        "end": end
    }