def channels_listall(token): """ Lists all the channels that exist. Paramaters: token (str): A string that validates the users actions while they are logged in. Returns: { channels: [] }: A list of all the channel dicitionaries. """ # raise InputError if inputed data is the wrong type if not isinstance(token, str): raise AccessError("Invalid token") # raise InputError if any of the inputs is empty if token == '': raise InputError("Invalid input") # check that token is authorised authenticate_token(token) return_list = [] for channel in data.channels: return_list.append({'channel_id': channel.channel_id, 'name': channel.name}) return { 'channels' : return_list }
def channel_messages(token, channel_id, start): """ Returns a list of messages, start and end. Parameters: token (str): a unique token (to determine authorisation) channel_id(int): a unique id for such channel start (int): index indicating when the messages start Returns: { messages (str): the message which will be sent in the channel start (int): index indicating when the messages start end (int): index indicating when the messages end } """ # Check that token is valid and gets it index(u_id) user_id = authenticate_token(token) user = valid_user_id(user_id) # Check that channel_id is valid channel = valid_channel_id(channel_id) # Check that user is part of the desired channel if not channel.existing_member(user): raise AccessError(description="User not in desired channel.") # Check that start is not greater # than the total number of messages in the channel and not negative msg_count = channel.num_messages() if (start > msg_count or start < 0): raise InputError("invalid start") # Initialize the desired return data ch_messages = {} ch_messages['messages'] = [] msg_load = msg_count - start if msg_count == 0: # No messages to load end = -1 elif start == msg_count: # Only loads a single message if start is equal to message_count msg = channel.channel_messages[msg_count - 1].message_details() ch_messages['messages'].append(msg) end = -1 elif msg_load <= 50: # Loads all the messages in the channel if there are less than 50 messages to load for i in range(msg_load, start, -1): msg = channel.channel_messages[i - 1].message_details() ch_messages['messages'].append(msg) end = -1 else: # Only loads the first 50 messages if there are more than 50 messages in the channel for i in range(start + 50, start, -1): msg = channel.channel_messages[i - 1].message_details() ch_messages['messages'].append(msg) end = start + 50 # Updates the start and end value which needs to be returned ch_messages['start'] = start ch_messages['end'] = end return ch_messages
def channel_join(token, channel_id): """ Adds a user from a channel, User should have been authenticated and authorised to join the channel. Parameters: token (str): a unique token (to determine authorisation) channel_id(int): a unique id for such channel Returns: {} """ # Check that token is valid user_id = authenticate_token(token) user = valid_user_id(user_id) # Check that channel_id is valid channel = valid_channel_id(channel_id) # Check that user is allowed to join if not channel.is_public and not user.permission_id == 1: raise AccessError("Channel is private, unable to join") # Check that the user is not in the channel yet if channel.existing_member(user): raise InputError(description="User is already a member.") # Adds user to the channel channel.new_member(user) # Promote to owner if user is flockr owner if user.permission_id == 1: channel.new_owner(user) return {}
def channel_leave(token, channel_id): """ Removes a user from a channel, User should have been authenticated and should be a member of this channel Parameters: token (str): a unique token (to determine authorisation) channel_id(int): a unique id for such channel Returns: {} """ # Check that token is valid and gets it index(u_id) user_id = authenticate_token(token) user = valid_user_id(user_id) # Check that channel_id is valid channel = valid_channel_id(channel_id) # Check that user is a member of the channel if not channel.existing_member(user): raise AccessError(description="User not in desired channel.") # Removes user from all_members list channel.remove_member(user) # Check if user is owner and remove from owner_members if TRUE if user.u_id in [ user['u_id'] for user in channel.channel_details()['owner_members'] ]: channel.remove_owner(user) return {}
def standup_send(token, channel_id, message): """ Add a message to the queue that will be collated after the timer tuns out Parameters: token (str): Check that the user is validated to call. channel_id (int): Specifies which channel the standup is to be in. message (str): The message that is to be added. returns: {} """ user_id = authenticate_token(token) user = valid_user_id(user_id) channel = valid_channel_id(channel_id) standup = channel.standup_details() # Check that user is in this channel if not channel.existing_member(user): raise AccessError('User is not in the target channel') # Checking for invalid input if len(message) > 1000: raise InputError(description="Mesage too long") # Check that the timer is running if standup['time_finish'] is None: raise InputError(description="Standup not active") # Get senders handle handle = user_profile(token, user_id)['user']['handle_str'] # Add the message queue channel.standup_message_add(handle, message) return {}
def message_remove(token, message_id): """ Removes a message from the channel where message_id is found, Function only empties out the message, it does not remove it from the whole system. Parameters: token (str): Used to authenticate and identify the user message_id (int): the unique id of a message in the system Return: {} """ # Check if message exists in the data # Function will raise InputError if message does not exist msg_check = check_message_valid(message_id) ch_index = msg_check['ch_index'] # Check if token is valid user_id = authenticate_token(token) # Check if message_remove does not raise AccessError check_message_access(user_id, msg_check) # Removes the message from the channel data.channels[ch_index].channel_messages.remove(msg_check['sent_msg']) return {}
def user_profile_setemail(token, email): """ Changes and sets user's email. Parameters: token(str): A string that validates the users actions while They are logged in email(str): The new email of the user Returns: None """ # raise InputError if any of the inputs is empty if token == '': raise InputError("token input left empty") if email == '': raise InputError("email input left empty") # raise InputError if any of the inputs is an invalid type if not isinstance(token, str): raise AccessError("token of wrong data type.") if not isinstance(email, str): raise InputError("email of wrong data type.") # Checks that email is in valid format valid_email(email) # Checks that email is not being used by another user existing_email(email) # check that token is authorised tok = authenticate_token(token) user = data.users[tok] user.email = email return {}
def message_edit(token, message_id, message): """ Edits a message from the channel where message_id is found, Function willremove the messaage from the whole system if given message is empty Parameters: token (str): Used to authenticate and identify the user message_id (int): the unique id of a message in the system message (str): the string containing the new message Return: {} """ # Check if message exists in the data # Function will raise InputError if message does not exist msg_check = check_message_valid(message_id) ch_index = msg_check['ch_index'] msg_index = msg_check['msg_index'] channel = valid_channel_id(ch_index) # Check if token is valid user_id = authenticate_token(token) # Check if message_edit does not raise AccessError check_message_access(user_id, msg_check) # Edits the message or remove it if message is empty if message == '': channel.channel_messages.remove(msg_check['sent_msg']) else: channel.channel_messages[msg_index].update_message(message) return {}
def message_unreact(token, message_id, react_id): """ Given a valid user appointed with the token, unreact to an already reacted message under the user's u_id. If the given message does not have any of the react yet, it will raise an InputError. Parameters: token (str): Used to authenticate and identify the user message_id (int): the unique id of a message in the system react_id (int): the unique id of a 'react' that will be reacted to Return: {} """ # Check if token is valid user_id = authenticate_token(token) user = valid_user_id(user_id) # Check if message exists in the data msg_check = check_message_valid(message_id) ch_index = msg_check['ch_index'] msg_index = msg_check['msg_index'] channel = valid_channel_id(ch_index) # Check if user is a member of the channel where the message was posted if not channel.existing_member(user): raise InputError(description="User is not in desired channel") # Check if the given react_id is a valid react_id, function will raise InputError if it is invalid check_valid_react_id(react_id) # Find the current user_id in the reacts u_id list cur_msg = data.channels[ch_index].channel_messages[msg_index] is_reacted = False for react in cur_msg.reacts: if react['react_id'] == react_id and user_id in react['u_ids']: is_reacted = True # Check if the current message has been reacted by the user previously if is_reacted is not True: print("Here") raise InputError( description="Message has not been reacted by the user") # Get the index of the react_id in the list of reacts react_index = find_react_id_index(cur_msg.reacts, react_id) # Remove the current u_id from the u_ids list in the reacts dictionary cur_react_dict = cur_msg.reacts[react_index] cur_react_dict['u_ids'].remove(user_id) # Updates the is_this_user_reacted value if the current user who unreacted is the # same user who sent the message if user_id == cur_msg.u_id: cur_msg.reacts[react_index]['is_this_user_reacted'] = False # Remove the react from the whole reacts list if u_ids list is empty if cur_msg.reacts[react_index]['u_ids'] == []: cur_msg.reacts.pop(react_index) return {}
def channel_details(token, channel_id): """ Provides basic details about the channel a user is in. Parameters: token (str): a unique token (to determine authorisation) channel_id(int): a unique id for such channel Returns: { name (str): The name of the channel owner_members (list): List of dictionaries of the owner memebr of the channel, where each dictionary contains types { u_id, name_first, name_last } all_members (list): List of dictionaries of all the member in the channel, where each dictionary contains types { u_id, name_first, name_last } } """ # Check token and find index, aka u_id u_id = authenticate_token(token) user = valid_user_id(u_id) channel = valid_channel_id(channel_id) if not channel.existing_member(user): raise AccessError(description="User not in desired channel.") return channel.channel_details()
def message_unpin(token, message_id): """ Sets a message to unpinned by setting is_pinned to False Parameters: token (str): Used to authenticate and identify the user message_id (int): the unique id of a message in the system Return: {} """ # Check if token is valid user_id = authenticate_token(token) user = valid_user_id(user_id) # Check if message exists in the data # Function will raise InputError if message does not exist msg_check = check_message_valid(message_id) ch_index = msg_check['ch_index'] msg_index = msg_check['msg_index'] channel = valid_channel_id(ch_index) # Check if user is an owner of the channel, raise AccessError if not if user not in channel.owner_members: raise AccessError("User not not an owner inside desired channel") # check is pinned and set if channel.channel_messages[msg_index].is_pinned: channel.channel_messages[msg_index].is_pinned = False else: raise InputError(description="message wasn't pinned") return {}
def channel_invite(token, channel_id, u_id): """ Invites user into channel. Parameters: token (str): a unique token (to determine authorisation) channel_id(int): a unique id for such channel u_id (int): A unique identidier to indicate the target of this function Return: {} """ # raise InputError if any of the inputs is empty if token == '': raise InputError("Invalid Input") if channel_id == '': raise InputError("Invalid Input") if u_id == '': raise InputError("Invalid Input") # raise InputError if any of the inputs is an invalid type if not isinstance(token, str): raise AccessError("Token must be string") if not isinstance(channel_id, int): raise InputError("Channel_id must be integer") if not isinstance(u_id, int): raise InputError("u_id must be integer") # check that token is authorised caller_id = authenticate_token(token) caller = valid_user_id(caller_id) # Check that target exists target = valid_user_id(u_id) # raises InputError if channel_id doesn't refer to a valid channel channel = valid_channel_id(channel_id) # raises AccessError if caller is not member of channel if not channel.existing_member(caller): raise AccessError(description="Caller is not in target channel") # rasies InputError if target already a member if channel.existing_member(target): raise InputError(description="Target is already a member.") # Add user to channel channel.new_member(target) # if the new_member is also a global owner they need to be added as an owner_member if target.permission_id == 1: channel.new_owner(target) return {}
def standup_active(token, channel_id): """ Checks if a timer to timed_send is currently running. Parameters: token (str): Checks that the user is validated to call the function channel_id (int): Needed to check the right channel Returns: { is_active (bool): Is there a standup running in that channel? time_finish (UNIX timestamp): If yes, when does it end, else None } """ authenticate_token(token) channel = valid_channel_id(channel_id) standup = channel.standup_details() return { 'time_finish': standup['time_finish'], 'is_active': standup['is_active'] }
def user_profile(token, u_id): """ Function that returns the profile of the user with the given u_id as a dictionary. Parameters: token (str): Used to validate the users login session Return: { user: { u_id (int): Unique identification number of target email (str): email address of target name_first (str): first name of target name_last (str): last name of target handle_str (str): Not sure what this is used for }, } """ valid_user_id(u_id) authenticate_token(token) return {'user': data.users[u_id].user_details()}
def channels_create(token, name, is_public): """ Creates a channel. Paramaters: token (str): A string that validates the users actions while they are logged in. name (str): A string with the name of the channel. is_public (boolean): A boolean indicating if the channel is public or private. Returns: { 'channel_id' : (int) }: A directory containg the id of the new channel. """ # raise InputError if inputed data is the wrong type if not isinstance(token, str): raise AccessError("Invalid token") if not isinstance(name, str): raise InputError("Invalid input") if not isinstance(is_public, bool): raise InputError("Invalid input for bool") # raise InputError if any of the inputs is empty if token == '': raise InputError("Invalid input") if name == '': raise InputError("Invalid input") # raise InputError if the name is more than 20 characters if len(name) > 20: raise InputError("Invalid input") # check that token is authorised u_id = authenticate_token(token) # initlise channel new_channel = channel(name, is_public) # get user_id, first_name,last_name from token creator = data.users[u_id] new_channel.new_owner(creator) new_channel.new_member(creator) data.new_channel(new_channel) return { 'channel_id' : new_channel.channel_id }
def user_profile_uploadphoto(token, img_url, x_start, y_start, x_end, y_end, host_url): """ Upoloads a photo for the users profile. Parameters: token(str): A string that validates the users actions while they are logged in img_url(str): URL of an image on the internet x_start(int): x position to start cropping y_start(int): y position to start cropping x_end(int): x position to end cropping y_end(int): y position to end cropping Returns: {} """ # check that token is authorised user_id = authenticate_token(token) path = "src/static/profile_img_for_" + str(user_id) + ".jpg" # get image try: urllib.request.urlretrieve(img_url, path) except: raise InputError("invalid url") img = Image.open(path) width, height = img.size if x_start < 0 or x_start > width: raise InputError("Invalid x_start") elif y_start < 0 or y_start > height: raise InputError("Invalid y_start") elif x_end > width or x_end < x_start: raise InputError("Invalid x_end") elif y_end > height or y_end < y_start: raise InputError("Invalid x_end") if img.format != "JPEG": raise InputError("not a JPEG file") img_cropped = img.crop((x_start, y_start, x_end, y_end)) img_cropped.save(path) data.users[user_id].update_profile_img_url(host_url + path) return {}
def message_send(token, channel_id, message_in): """ Sends a message from an authenticated user to a valid channel the user is in Generates a message_id and returns it. Parameters: token (str): Used to authenticate and identify the user channel_id (int): the unique id of a channel in the system message (str): the message which will be sent in the channel Return: message_id (int): the unique id of a message in the system """ # Check message length, raise InputError if characters in message: # -> length of characters > 1000 # -> length of characters == 0 (empty message) if len(message_in) > 1000 or len(message_in) == 0: raise InputError(description='Invalid message length') # Check if token and channel_id exists in data user_id = authenticate_token(token) user = valid_user_id(user_id) channel = valid_channel_id(channel_id) # Check if user is in the channel, raise AccessError if user is not in channel if not channel.existing_member(user): raise AccessError("User not in desired channel") # Check if message is related to hangman and edit it accordingly if it is msg_check = message_in.split() is_guess = False if msg_check[0] == "/guess" and channel.hangman.get_details()['mode']: if len(msg_check) == 2: message_in = msg_check[1] is_guess = True if message_in == "/hangman start" or is_guess: message_in = hangman(message_in, channel) message_id = data.message_index data.message_index += 1 message_object = message(message_in, user_id, message_id) channel.new_message(message_object) return {'message_id': message_object.message_id}
def message_sendlater(token, channel_id, message, time_sent): """ Sends a message from an authenticated user to a valid channel the user is in Generates a message_id and returns it at the specified time_sent in the future. Parameters: token (str): Used to authenticate and identify the user channel_id (int): the unique id of a channel in the system message (str): the message which will be sent in the channel time_sent (int): unix integer timestamp for a given time (consist of year, month, day and time) Return: message_id (int): the unique id of a message in the system """ # Check message length, raise InputError if characters in message is invalid if len(message) > 1000 or len(message) == 0: raise InputError(description='Invalid message length') # Check if token is valid and channel_id exists in data user_id = authenticate_token(token) channel = valid_channel_id(channel_id) user = valid_user_id(user_id) # Check if the time_sent is not a time in the past cur_time = int(time.time()) if time_sent < cur_time: raise InputError(description='Time sent is a time in the past') # Check if user is in the channel, raise AccessError if user is not in channel if not channel.existing_member(user): raise AccessError("User not in desired channel") # Generate message id and increment the counter in the data # Message_id is generated when message_sendlater is called msg_id = data.message_index data.message_index += 1 # Send the message according to the desired time timer = time_sent - cur_time send_after = threading.Timer( timer, send_msg, [msg_id, user_id, message, channel, time_sent]) send_after.start() # Return the generated message_id return {'message_id': msg_id}
def channel_addowner(token, channel_id, u_id): """ Function that adds target user as owner. If global owner, then bypass check of caller being an owner or target being member. Parameters: token (str): A unique token to determine authorisation channel_id (int): A unique identifier of channels u_id (int): A unique identidier to indicate the target of this function Returns: {} """ # Check that token is valid caller_id = authenticate_token(token) caller = valid_user_id(caller_id) target = valid_user_id(u_id) # Check that channel_id is valid channel = valid_channel_id(channel_id) # Check that the caller is a member and an owner if caller.u_id not in [ user['u_id'] for user in channel.channel_details()['owner_members'] ]: raise AccessError(description="Caller is not an owner / member") # Check that the target is a member (If global owner, make member first) if not channel.existing_member(target): if target.permission_id == 1: channel.new_member(target) else: raise InputError(description="Target is not a member") # Check that not targeted at an owner if target.u_id in [ user['u_id'] for user in channel.channel_details()['owner_members'] ]: raise InputError(description="Target is already an owner") # If reached, here then successful channel.new_owner(target) return {}
def channel_removeowner(token, channel_id, u_id): """ Function that removes target user as owner. Global owner cannot be removed except for by another global owner. Parameters: token (str): A unique token to determine authorisation channel_id (int): A unique identifier of channels u_id (int): A unique identidier to indicate the target of this function Returns: {} """ # Check that token is valid caller_id = authenticate_token(token) caller = valid_user_id(caller_id) target = valid_user_id(u_id) # Check that channel_id is valid channel = valid_channel_id(channel_id) # Check that caller is a member if not channel.existing_member(caller): raise AccessError(description="Caller not in channel") # Check that access is from an owner and targeted at an owner if caller.u_id not in [ user['u_id'] for user in channel.channel_details()['owner_members'] ]: raise AccessError(description="Caller is not an owner") if target.u_id not in [ user['u_id'] for user in channel.channel_details()['owner_members'] ]: raise InputError(description="Target is not an owner") # Only a global owner can remove a global owner if caller.permission_id != 1 and target.permission_id == 1: raise AccessError('Need global permissions to remove global owner') # If reached, here then successful channel.remove_owner(target) return {}
def user_profile_setname(token, name_first, name_last): """ Changes and sets users first and last name. Parameters: token(str): A string that validates the users actions while they are logged in name_first(str): The new first name of user name_last(string): The new last name of user Returns: None """ # raise InputError if any of the inputs is empty if token == '': raise InputError("token input left empty") if name_first == '': raise InputError("name_first left empty") if name_last == '': raise InputError("name_last left empty") # raise InputError if any of the inputs is an invalid type if not isinstance(token, str): raise AccessError("token of wrong data type") if not isinstance(name_first, str): raise InputError("name_first of wrong data type") if not isinstance(name_last, str): raise InputError("name_last of wrong data type") # name_first is not between 1 and 50 characters or # name_last is not between 1 and 50 characters if (len(name_first) > 50 or len(name_first) < 1): raise InputError("name_first length is out of range") if (len(name_last) > 50 or len(name_last) < 1): raise InputError("name_last length is out of range") # check that token is authorised tok = authenticate_token(token) user = data.users[tok] user.name_first = name_first user.name_last = name_last return {}
def channels_list(token): """ Lists all the channels a user is a part of. Paramaters: token (str): A string that validates the users actions while they are logged in. Returns: { channels (list): A list of all the channel dicitionaries which the user is in. } """ # raise InputError if inputed data is the wrong type if not isinstance(token, str): raise AccessError("Invalid token") # raise InputError if any of the inputs is empty if token == '': raise InputError("Invalid token") # check that token is authorised token_index = authenticate_token(token) user_id = data.users[token_index].u_id channels_list_array = [] # if user is memember add channel to list for channel in data.channels: for user in channel.all_members: if user.u_id == user_id: channels_list_array.append({'channel_id': channel.channel_id, 'name': channel.name}) break return { 'channels': channels_list_array }
def standup_start(token, channel_id, length): """ Starts the timer that calls timed_send when it finishes Parameters: token (str): Check that the user is validated to call. channel_id (int): Specifies which channel the standup is to be in. length (int): Specifies the duration of the standup ( > 0). Returns: { time_finish (UNIX timestamp): When will timed_send be called? } """ u_id = authenticate_token(token) user = valid_user_id(u_id) # Check valid channel and user belongs to channel channel = valid_channel_id(channel_id) if not channel.existing_member(user): raise AccessError('User is not in the target channel') # Check that the object is not currently running if standup_active(token, channel_id)['is_active']: raise InputError(description="Standup already in progress") # Check length is positive if length <= 0: raise InputError("Duration must be positive") # Start the timer if reached here timer = threading.Timer(length, timed_send, [token, channel_id]) timer.start() current_time = int(time.time()) added = current_time + length channel.standup_end = added return {'time_finish': channel.standup_end}
def user_profile_sethandle(token, handle_str): """ Changes and sets user's handle_str. Parameters: token(str): A string that validates the users actions while they are logged in handle_str(str): The handle the user wants to change to Returns: None """ # raise InputError if any of the inputs is empty if token == '': raise InputError("Token input left empty") if handle_str == '': raise InputError("Handle_str input left empty") # raise InputError if any of the inputs is an invalid type if not isinstance(token, str): raise AccessError("Invalid Token") if not isinstance(handle_str, str): raise InputError("Invalid Email") # raises an error if the handle is not of the required length if len(handle_str) < 3: raise InputError("handle string too short") elif len(handle_str) > 20: raise InputError("handle string too long") # check that token is authorised tok = authenticate_token(token) # check that handle_str is not being used by another user existing_handle(handle_str) user = data.users[tok] user.handle_str = handle_str return {}
def message_react(token, message_id, react_id): """ Given a valid user appointed with the token, react to an existing message under the user's u_id. If the given message does not have any of the react yet, it will create append a new dictionary for the react_id. Parameters: token (str): Used to authenticate and identify the user message_id (int): the unique id of a message in the system react_id (int): the unique id of a 'react' that will be reacted to Return: {} """ # Check if token is valid user_id = authenticate_token(token) user = valid_user_id(user_id) # Check if message exists in the data msg_check = check_message_valid(message_id) ch_index = msg_check['ch_index'] msg_index = msg_check['msg_index'] channel = valid_channel_id(ch_index) # Check if user is a member of the channel where the message was posted if not channel.existing_member(user): raise InputError(description="User is not in desired channel") # Check if the given react_id is a valid react_id, function will raise InputError if it is invalid check_valid_react_id(react_id) # Find the current user_id in the reacts u_id list cur_msg = data.channels[ch_index].channel_messages[msg_index] is_already_reacted = False for react in cur_msg.reacts: if react['react_id'] == react_id and user_id in react['u_ids']: is_already_reacted = True # Check if the current message has not been reacted by the user previously if is_already_reacted is True: raise InputError( description="Message has already been reacted by the user") # Get the index of the react_id in the list of reacts react_index = find_react_id_index(cur_msg.reacts, react_id) # react_id has yet to exist in the message, create a new react dictionary # with the current user data to append to the reacts list if react_index == -1: new_react_dict = {} new_react_dict['react_id'] = react_id new_react_dict['u_ids'] = [user_id] new_react_dict['is_this_user_reacted'] = False cur_msg.reacts.append(new_react_dict) else: # Add react data on the current dictionary cur_react_dict = cur_msg.reacts[react_index] # Append the current user_id into the u_ids list cur_react_dict['u_ids'].append(user_id) # Updates the is_this_user_reacted value if the current user who reacted is the # same user who sent the message if user_id == cur_msg.u_id: cur_msg.reacts[react_index]['is_this_user_reacted'] = True return {}
def test_authenticate_token_valid_token(): clear() user1 = auth_register('*****@*****.**', 'password', 'Test', 'Test') returned_id = authenticate_token(user1['token']) assert returned_id == user1['u_id']
def test_authenticate_token_invalid_token(): clear() user1 = auth_register('*****@*****.**', 'password', 'Test', 'Test') auth_logout(user1['token']) with pytest.raises(AccessError): authenticate_token(user1['token'])