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 test_valid_channel_id_valid_channel(): clear() user1 = auth_register('*****@*****.**', 'password', 'Test', 'Test') channel1 = channels_create(user1['token'], "Test Channel", True) result = valid_channel_id(channel1['channel_id']) assert result.name == "Test Channel"
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 timed_send(token, channel_id): """ Called at the end of the timer set in the standup_start function. Calls message send, after checking some conditions. Parameters: token (str): Passed on to message_send. channel_id (int) Passed on to message_send. Also needed for channel data Returns: None """ channel = valid_channel_id(channel_id) # Get standup details and reset standup = channel.standup_details() channel.standup_reset() # Abort if no message has been queued if standup['message_queue'] == "": return # Call message send otherwise, truncating trailing newline characters message_send(token, channel_id, standup['message_queue']) 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 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 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 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 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 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 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 {}