Example #1
0
def channels_leave(token, channel_id):
    """
        Removes the caller from the specified channel.
        Parameters:  
            token      (str)
            channel_id (str)
        Returns: 
            {}
    """
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    channels_list = Channel.query.all()
    selected_channel = select_channel(channel_id)
    # Check if Channel ID is not a valid channel
    if not selected_channel:
        raise InputError(description="Channel ID is not a valid channel")
    # Check user if the user is not currently a member of the selected channel
    if not is_user_member(user, selected_channel):
        raise AccessError(description="You are not a member of this channel")

    membership = MemberOf.query.filter_by(user_id=user.id, channel_id=selected_channel.id).first()
    db.session.delete(membership)
    db.session.commit()
    # If the user attempting to leave is the owner... Pass down ownership to someone else? Or delete channel
    # TODO:
    # If there is no members left in channel, delete channel
    # TODO:
    return {}
Example #2
0
def message_send(token, channel_id, message):
    """
        Sends a message to the selected channel.
        Parameters:
            token      (str)
            channel_id (int)
            message    (str)
        Returns: 
            { message_id }
    """
    verify_token(token)
    if not message:
        raise InputError("Message can't be empty")
    if len(message) > 1000:
        raise InputError("Message is over 1000 characters")
    user = get_user_from_token(token)
    selected_channel = select_channel(channel_id)
    # Raise error if the user is not a part of the channel they are trying to message in
    if not is_user_member(user, selected_channel):
        raise AccessError(
            description="User is not a member of channel with channel_id")

    sent_message = Message(channel=selected_channel,
                           user=user,
                           message=message)
    db.session.add(sent_message)
    db.session.commit()
    return {"message_id": sent_message.id}
Example #3
0
def channels_join(token, channel_id):
    """
        Given a channel_id of a channel that the authorised user can join, adds them to that channel
        Parameters:  
            token      (str)
            channel_id (int)
        Returns:
            {}
    """
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description="Target channel is not a valid channel")
    # Check whether channel is private or not. Raise AccessError if it is
    if not selected_channel.visibility == "public":
        raise AccessError(description="Target channel isn't public")

    new_membership = MemberOf(
        user=user,
        channel=selected_channel,
        is_owner=False
    )
    db.session.add(new_membership)
    db.session.commit()
    return {}
Example #4
0
def channels_messages(token, channel_id, start, limit=50):
    """
        Given a channel that the user is a member of, returns up to a specified maximum
        number of messages from a starting index. Messages are ordered in ascending
        recency, so the message at index 0 is the most recent.
        Parameters:
            token      (str)
            channel_id (int)
            start      (int)
            limit      (int)
        Returns:
            { messages, exhausted }
        Where:
            messages: list of message dictionary: { message_id, user_id, message, time_created, is_author }
            exhausted: bool indicating whether the there any more messages left to fetch 
    """
    # check parameters are all valid and raise exception if they aren't
    # add user_id, associated first name and last name into channel_id dictionary (or storage)
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description="Channel ID is not a valid channel")
    if is_user_member(user, selected_channel) is False:
        raise AccessError(description="You are not a member of this channel")

    # Loop through 50 message dictionaries of list starting from start index
    messsages_list = selected_channel.messages_sent
    messsages_list.sort(key=(lambda x: x.time_created))
    
    payload = {
        "messages": [],
        "exhausted": True
    }
    
    if not messsages_list:
        # printColour("Results: {}".format(payload), colour="blue")
        return payload
    if start >= len(messsages_list):
        raise InputError(description="Starting index is greater than or equal to the total number of messages in the channel")
    if not (start + limit >= len(messsages_list)):
        messages["exhausted"] = False
    
    for i, message in enumerate(messsages_list[start :], start=1):
        payload["messages"].append({
            "message_id": message.id,
            "user_id": message.user_id,
            "message": message.message,
            "time_created": message.time_created.timestamp(),
            "is_author": message.user_id == user.id
        })
        if i == limit:
            break
    # printColour("Results: {}".format(payload), colour="blue")
    return payload
Example #5
0
def channels_removeowner(token, channel_id, user_id):
    """
        Desc:    Remove user with user id user_id an owner of this channel
        Params:  (token, channel_id, user_id)
        Returns: empty dict
        Errors: InputError on invalid channel_id
                InputError when user_id DOESN'T already have ownership over
                a channel before calling channel_removeowner
                AccessError when the user isn't an owner of the
                slackr or an owner of this channel

        TYPES:
        token             str
        channel_id        int
        user_id              int
    """
    verify_token(token)
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description="Not a valid channel")
    calling_user = get_user_from_token(token)

    details = channels_details(token, channel_id)
    # Check whether the target user is actually in the list of members
    members_list = details["all_members"]
    user_found = False
    for member in members_list:
        if member["user_id"] == user_id:
            user_found = True
            break
    if not user_found:
        raise InputError("Target user must be a part of this channel")

    # Check whether user_id is not already an owner in channel
    owners_list = details["owner_members"]
    calling_user_is_owner = False
    target_user_is_owner = False
    for owner in owners_list:
        if owner["user_id"] == user_id:
            target_user_is_owner = True
        if owner["user_id"] == calling_user.id:
            calling_user_is_owner = True
    if not calling_user_is_owner:
        raise InputError("You are not authorised to remove existing owners")
    if not target_user_is_owner:
        raise InputError("Target user is not an owner of the channel")

    target_user = User.query.filter_by(id=user_id).first()
    for each_membership in target_user.channel_membership:
        if each_membership.channel_id == channel_id:
            each_membership.is_owner = False
    db.session.commit()
    return {}
Example #6
0
def channels_details(token, channel_id):
    """
        Given a Channel with ID channel_id that the authorised user is
        part of, provide basic details about the channel
        Parameters:
            token       (str)
            channel_id  (channel_id)
        Returns:
            { name, description, visibility, channel_img_url, owner_members, all_members }    (dict)
        Where:
            owner_members: [{ user_id, username, email, profile_img_url }, ...]  (list of user objects)
            all_members: [{ user_id, username, email, profile_img_url }, ...]    (list of user objects)
    """
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description=f"{channel_id} doesn't point to a valid channel")
    # Raise exception when the user is not a member of the channel with the given channel_id
    if not is_user_member(user, selected_channel):
        raise AccessError(description="You are not a member of this channel")

    channel_owners = []
    channel_members = []
    # Joining User with MemberOf, then filtering for users associated with the selected channel 
    # TODO: Possible optimisation -> swap the ordering of the filtering
    memberships = db.session.query(User, MemberOf, Channel).outerjoin(MemberOf, MemberOf.user_id==User.id).outerjoin(Channel, Channel.id==MemberOf.channel_id).filter_by(id=channel_id).all()    
    for each_membership in memberships:
        curr_member = each_membership[0]
        member_data = {
            "user_id": curr_member.id,
            "username": curr_member.username,
            "profile_img_url": curr_member.bio.profile_img_url,
            "email": curr_member.email
        }
        channel_members.append(member_data)
        if each_membership[1].is_owner:
            channel_owners.append(member_data)
    
    details = {
        "name": selected_channel.name,
        "description": selected_channel.description,
        "visibility": selected_channel.visibility,
        "channel_img_url": selected_channel.channel_img_url,
        "channel_cover_img_url": selected_channel.channel_cover_img_url,
        "owner_members": channel_owners,
        "all_members": channel_members
    }
    # printColour("Results: {}".format(details), colour="blue")
    return details
Example #7
0
def handle_channel_details():
    """
        HTTP Route: /channels/details
        HTTP Method: GET
        Params: (token, channel_id)
        Returns JSON: { name, description, visibility, channel_img_url, owner_members, all_members }
    """
    token = request.args.get("token")
    channel_id = int(request.args.get("channel_id"))

    target_channel = select_channel(channel_id)
    calling_user = get_user_from_token(token)

    printColour(" ➤ Channel Details: {}'s details fetched by {}".format(
        target_channel.name, calling_user.username),
                colour="blue")
    return jsonify(channels.channels_details(token, channel_id))
Example #8
0
def handle_channel_leave():
    """
        HTTP Route: /channels/leave
        HTTP Method: POST
        Params: (token, channel_id)
        Returns JSON: {  }
    """
    request_data = request.get_json()
    token = request_data["token"]
    channel_id = int(request_data["channel_id"])

    target_channel = select_channel(channel_id)
    calling_user = get_user_from_token(token)

    printColour(" ➤ Channel Leave: {} left channel {}".format(
        calling_user.username, target_channel.name),
                colour="blue")
    return jsonify(channels.channels_leave(token, channel_id))
Example #9
0
def handle_channel_messages():
    """
        HTTP Route: /channels/messages
        HTTP Method: GET
        Params: (token, channel_id, start)
        Returns JSON: { messages, exhausted }
    """
    token = request.args.get("token")
    channel_id = int(request.args.get("channel_id"))
    start = int(request.args.get("start"))

    target_channel = select_channel(channel_id)
    calling_user = get_user_from_token(token)

    printColour(" ➤ Channel Messages: {}'s messages fetched by {}".format(
        target_channel.name, calling_user.username),
                colour="blue")
    return jsonify(channels.channels_messages(token, channel_id, start))
Example #10
0
def channels_invite(token, channel_id, user_id):
    """
        Invites a user, with the given user_id, to join the channel with ID channel_id.
        Once invited, the user is added to the channel immediately.
        Parameters:
            token       (str)
            channel_id  (int)
            user_id     (int)
        Returns: 
            {}          (dict)
    """
    printColour("RECEIVED CHANNEL ID: {}".format(channel_id), colour="blue")
    printColour("RECEIVED USER ID: {}".format(user_id), colour="blue")
    verify_token(token)
    calling_user = get_user_from_token(token)
    if not calling_user:
        raise AccessError(description="Invalid Token")
    
    # If channel_id is invalid, raise input error
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description="Target channel is not a valid channel that the user is part of.")
    # Check the authorised user is not already a member of the channel
    if not is_user_member(calling_user, selected_channel):
        raise AccessError(description="You are not a member of this channel")

    invited_user = User.query.filter_by(id=user_id).first() 
    # Check that the user exists (ie. the user_id is valid)
    if not invited_user:
        raise InputError(description="Target user is not a valid user")
    # Check if invited_user is already a member
    if is_user_member(invited_user, selected_channel):
        raise InputError(description="{} is already a member".format(invited_user.username))
    # Granting membership
    new_membership = MemberOf(
        user=invited_user,
        channel=selected_channel,
        is_owner=False
    )
    printColour("Trying to add user {} to channel {}".format(new_membership.user.id, new_membership.channel.id), colour="blue")
    
    db.session.add(new_membership)
    db.session.commit()
    return {}
Example #11
0
def handle_channels_upload_image():
    """
        HTTP Route: /channels/uploadimage
        HTTP Method: POST
        Params: (token, channel_id, x_start, y_start, x_end, y_end, file)
    """
    token = request.form["token"]
    channel_id = request.form["channel_id"]
    x_start = int(request.form["x_start"])
    y_start = int(request.form["y_start"])
    x_end = int(request.form["x_end"])
    y_end = int(request.form["y_end"])

    calling_user = get_user_from_token(token)
    target_channel = select_channel(channel_id)
    printColour(
        " ➤ Channel Upload Image: {} uploading channel image for {}".format(
            calling_user.username, target_channel.name),
        colour="blue")
    printColour(" ➤ Crop coordinates: start = ({}, {}), end = ({}, {})".format(
        x_start, y_start, x_end, y_end),
                colour="cyan")

    # Check if the post request has the file part
    if 'file' not in request.files:
        printColour(" ➤ User didn't upload a photo", colour="red")
        raise InputError("No valid image file found. Please try again")
    else:
        file = request.files['file']
        if file and allowed_file(file.filename):
            # Saving the image to local storage
            filename = get_latest_filename(
                "channel_{}_profile.jpg".format(channel_id))
            file.save(os.path.join(UPLOAD_FOLDER, filename))
            crop_image_file(filename, x_start, y_start, x_end, y_end)
            # Saving the image endpoint to the channel's tuple under the channel_img_url field
            image_endpoint = "{0}/images/{1}".format(BASE_URL, filename)
            channels.channels_upload_photo(token, channel_id, image_endpoint)
            printColour(
                " ➤ Successfully uploaded channel image for {}. Available at: {}"
                .format(target_channel.name, image_endpoint),
                colour="green",
                bordersOn=False)
            return jsonify({"succeeded": True})
Example #12
0
def handle_send_message(token, channel_id, message, room):
    calling_user = get_user_from_token(token)
    target_channel = select_channel(channel_id)
    printColour(
        " ➤ Socket event: send_message. Message sent by {} to channel {}".
        format(calling_user.username, target_channel.name),
        colour="cyan")
    printColour(" ➤ Emitting event: receive_message",
                colour="cyan",
                bordersOn=False)
    try:
        message_send(token, int(channel_id), message)
    except InputError as err:
        emit_error(err.get_message())
    # Broadcast the newly sent message to all listening client sockets so the
    # chat field updates in 'realtime'
    emit("receive_message",
         "The server says: someone has sent a new message",
         broadcast=True,
         room=room)
Example #13
0
def handle_channel_add_owner():
    """
        HTTP Route: /channels/addowner
        HTTP Method: POST
        Params: (token, channel_id, user_id)
        Returns JSON: {  }
    """
    request_data = request.get_json()
    token = request_data["token"]
    channel_id = int(request_data["channel_id"])
    user_id = int(request_data["user_id"])

    target_channel = select_channel(channel_id)
    calling_user = get_user_from_token(token)
    target_user = get_user_from_id(user_id)

    printColour(" ➤ Channel Add Owner: {} added {} as an owner in {}".format(
        calling_user.username, target_user.username, target_channel.name),
                colour="blue")
    return jsonify(channels.channels_addowner(token, channel_id, user_id))
Example #14
0
def handle_search():
    """
        HTTP Route: /channels/search
        HTTP Method: GET
        Params: (token, channel_id, query_str)
        Returns JSON: {
            messages: [ { message_id, u_id, message, time_created, reacts }, ...]
        }
    """
    token = request.args.get("token")
    channel_id = request.args.get("channel_id")
    query_str = request.args.get("query_str")

    calling_user = get_user_from_token(token)
    target_channel = select_channel(channel_id)

    printColour(
        " ➤ Channel Search: {} looking for '{}' in {}'s messages".format(
            calling_user.username, query_str, target_channel.name),
        colour="blue")
    return jsonify(messages.messages_search_match(token, channel_id,
                                                  query_str))
Example #15
0
def handle_channel_invite():
    """
        HTTP Route: /channels/invite
        HTTP Method: POST
        Params: (token, channel_id, user_id)
        Returns JSON: {  }
    """
    request_data = request.get_json()
    token = request_data["token"]
    channel_id = int(request_data["channel_id"])
    user_id = int(request_data["user_id"])

    target_channel = select_channel(channel_id)
    target_user = get_user_from_id(user_id)
    calling_user = get_user_from_token(token)

    printColour(
        " ➤ Channel Invite: invitation to {} ({}) sent from {} to {}".format(
            target_channel.name, target_channel.visibility,
            calling_user.username, target_user.username),
        colour="blue")
    return jsonify(channels.channels_invite(token, channel_id, user_id))
Example #16
0
def handle_channels_update_info():
    """
        HTTP Route: /channels/update
        HTTP Method: PUT
        Params: (token, channel_id, name, description, visibility)
        Returns JSON: { succeeded }
    """
    request_data = request.get_json()
    token = request_data["token"]
    channel_id = request_data["channel_id"]
    name = request_data["name"]
    description = request_data["description"]
    visibility = request_data["visibility"]

    calling_user = get_user_from_token(token)
    target_channel = select_channel(channel_id)

    printColour(" ➤ Channel Update: new fields: name={}, visibility={}".format(
        name, visibility),
                colour="blue")

    channels.channels_update_info(token, channel_id, name, description,
                                  visibility)
    return jsonify({"succeeded": True})