def dm_list_v1(token): """Summary Returns the list of DMs that the user is a member of Args: token (string): A user session token Returns: Dictionary: { dms } ({dm_id,name}) Contains the id and name of the dm Raises: AccessError: invaild token """ check_token(token) auth_user = user_from_token(token) # Find the desired dm, if it exists, and return all dm's name and id output = [] for dm in dms: if auth_user['u_id'] in dm['members']: id_name = {'dm_id': dm['dm_id'], 'name': dm['name']} output.append(id_name) return {'dms': output}
def channels_listall_v2(token): """Summary Provide a list of all channels (and their associated details) Args: token (string): A user session token Returns: Dictionary: Contains the list of all channels, and their associated details Raises: AccessError: When an invalid token is given """ check_token(token) # Collect channel information for each channel output = [] for channel in channels: channel_details = { 'channel_id': channel['channel_id'], 'name': channel['name'] } output.append(channel_details) return {'channels': output}
def standup_send_v1(token, channel_id, message): check_token(token) sender = user_from_token(token) u_id = sender['u_id'] # InputError when Channel ID is not a valid channel if valid_channel(channel_id) == False: raise InputError(f"Channel ID {channel_id} is not a valid channel") # InputError when Message is more than 1000 characters (not including the username and colon) if len(message) > 1000: raise InputError(f"Message more than 1000 characters") # AccessError when The authorised user is not a member of the channel that the message is within if is_channel_member(u_id, channel_id) == False: raise AccessError( f"The authorised user {u_id} is not a member of the channel that the message is within" ) for channel in channels: if channel['channel_id'] == channel_id: # InputError when An active standup is not currently running in this channel if channel['standup']['is_active'] == False: raise InputError( "An active standup is not currently running in this channel" ) else: message_dict = { 'author_handle': sender['handle_str'], 'message': message } channel['standup']['messages'].append(message_dict) return {}
def channel_addowner_v1(token, channel_id, u_id): ''' Summary: Make user with user id u_id an owner of this channel Args: token (string): A user session token channel_id (int): A channel_id number u_id (int): A user id number Returns: a empty dictionary Raises: InputError when: Channel ID is not a valid channel When user with user id u_id is already an owner of the channel AccessError when: the authorised user is not an owner of the **Dreams**, or an owner of this channel ''' global users, channels assert check_token(token) assert valid_user(u_id) if valid_channel(channel_id) == False: raise InputError(f"Channel ID is not a valid channel") auth_user = user_from_token(token) current_channel = find_channel( channel_id) #current channel with the given channel id if not is_channel_owner(auth_user['u_id'], channel_id) and not is_dreams_owner( auth_user['u_id']): raise AccessError( f"the authorised user is not an owner of the channel or Dreams") if is_channel_owner(u_id, channel_id) == True: raise InputError( f"User with user id {u_id} is already an owner of the channel") target_user = user_from_id(u_id) current_channel['owner_members'].append(u_id) if u_id not in current_channel['all_members']: current_channel['all_members'].append(u_id) target_user['channels'].append(channel_id) update_user_channel_stats(u_id) send_channel_added_notification(auth_user['handle_str'], u_id, current_channel['channel_id']) return {}
def dm_messages_v1(token, dm_id, start): """Summary Given a DM with ID dm_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. Args: token (string): A user session token dm_id (int): A dm id number start (int): The index in the dm's list of messages at which to begin collecting them Returns: TYPE: Contains the start index, the end index (-1 if the final message in the list was collected, or start + 50 if 50 messages were collected without reaching the end of the list) and the list of collected messages. Raises: AccessError: When an invalid token is given, or authorised user is not a member of channel with channel_id InputError: When dm_id does not refer to a valid dm or start is negative or greater than the total number of messages in the channel """ check_token(token) auth_user_id = user_from_token(token)['u_id'] # Retrieve messages from channel with given channel_id message_ids = None for dm in dms: if dm['dm_id'] == dm_id: if auth_user_id in dm['members']: message_ids = dm['messages'] else: raise AccessError( f"Authorised user is not a member of dm with id {dm_id}") if message_ids == None: raise InputError(f"dm id {dm_id} does not refer to a valid dm") messageList = [] for message in messages: if message['message_id'] in message_ids: messageList.append(message) # If the channel has no messages, return an empty list if start == 0 and not len(messageList): return {'messages': messageList, 'start': start, 'end': -1} # Check whether start index is negative if start < 0: raise InputError(f"start cannot be negative (given {start})") messageList.reverse() # Most recent message should have index 0 # Check whether start index is within bounds of message list if start < len(messageList): output = [] index = start while len(output) < 50: if index >= len(messageList): return {'messages': output, 'start': start, 'end': -1} message = messageList[index] # { react_id, u_ids, is_this_user_reacted } react_dict = { 'react_id': 1, 'u_ids': message['reacts'], 'is_this_user_reacted': auth_user_id in message['reacts'] } message_dict = {} message_dict['message_id'] = message['message_id'] message_dict['u_id'] = message['author_id'] message_dict['message'] = message['message'] message_dict['time_created'] = message['time_created'] message_dict['reacts'] = [react_dict] message_dict['is_pinned'] = message['is_pinned'] output.append(message_dict) index += 1 return { 'messages': output, 'start': start, 'end': start + 50 if len(messageList) > 50 else -1 } else: raise InputError( f"start index {start} is greater than the total number of messages in the channel {len(messages)}" )
def dm_create_v1(token, u_id): ''' Summary: u_ids is the user(s) that this DM is directed to, and will not include the creator. The creator is the owner of the DM. name should be automatically generated based on the user(s) that is in this dm. The name should be an alphabetically-sorted, comma-separated list of user handles, e.g. 'handle1, handle2, handle3'. Args: token (string): A user session token u_ids (list): A list of user ids Returns: a dictionary which contains the id of the dm (dm_id) and the name of the dm which is the concatenated handles Raises: InputError when: u_id does not refer to a valid user ''' global users, channels, next_dm_id, dms assert check_token(token) auth_user = user_from_token(token) user_list_handles = [auth_user['handle_str']] for i in range(len(u_id)): if valid_user(u_id[i]) == False: raise InputError(f"u_id {u_id[i]} does not refer to a valid user") else: member = user_from_id(u_id[i]) user_list_handles.append(member['handle_str']) name = join_handle(user_list_handles) dm_id = next_dm_id['id'] #give the current global id number to this dm next_dm_id['id'] += 1 #after add one so the the dm id is always unique dm_members = [auth_user['u_id']] for user_id in u_id: dm_members.append(user_id) # Add dm to each member's list of dms for user in users: if user['u_id'] in dm_members: user['dms'].append(dm_id) update_user_dm_stats(user['u_id']) new_dm = { 'dm_id': dm_id, 'name': name, 'creator': auth_user['u_id'], 'messages': [], 'members': dm_members } dms.append(new_dm) update_dm_stats() for user_id in new_dm['members']: send_dm_added_notification(auth_user['handle_str'], user_id, dm_id) return {'dm_id': dm_id, 'dm_name': new_dm['name']}
def admin_user_remove_v1(token, u_id): ''' Summary: Given a User by their user ID, remove the user from the Dreams. Dreams owners can remove other **Dreams** owners (including the original first owner). Once users are removed from **Dreams**, the contents of the messages they sent will be replaced by 'Removed user'. Their profile must still be retrievable with user/profile/v2, with their name replaced by 'Removed user'. Args: token (string): A user session token u_id (int): A user id number Returns: empty dictionary Raises: InputError when: u_id does not refer to a valid user The user is currently the only owner AccessError when: The authorised user is not an owner ''' global users, channels, tokens, dms assert check_token(token) auth_user = user_from_token(token) if valid_user(u_id) == False: raise InputError(f"User with u_id is not a valid user") dream_owners = find_all_dream_owners() #list of all dream owners if auth_user['permission'] != 1: raise AccessError("The authorised user is not an owner") elif auth_user['u_id'] == u_id and len(dream_owners) == 1: # Only way that user being removed can be only owner is if they are removing themself raise InputError("The user is currently the only owner") # Remove all tokens associated with user being removed cleared_tokens = [ tkn for tkn in tokens if user_from_token(tkn)['u_id'] != u_id ] tokens.clear() for tkn in cleared_tokens: tokens.append(tkn) user = user_from_id(u_id) user['name_first'] = 'Removed' user['name_last'] = 'user' user['permission'] = 0 # User permission id 0 indicates a removed user for channel in channels: for owner_member in channel['owner_members']: if owner_member == u_id: channel['owner_members'].remove(u_id) for normal_member in channel['all_members']: if normal_member == u_id: channel['all_members'].remove(u_id) for dm in dms: for member in dm['members']: if member == u_id: dm['members'].remove(u_id) for message in messages: if message['author_id'] == u_id: message['message'] = 'Removed user' return {}
def message_remove_v1(token, message_id): """Summary Given a message_id for a message, this message is removed from the channel/DM Args: token (string): A user session token message_id (int): A message id number Returns: Empty dictionary Raises: AccessError: when none of the following are true: Message with message_id was sent by the authorised user making this request The authorised user is an owner of this channel (if it was sent to a channel) or the **Dreams** InputError: Message (based on ID) no longer exists """ check_token(token) auth_user = user_from_token(token) is_channel_message = False for msg in messages: if msg['message_id'] == message_id: is_channel_message = (msg['channel_id'] != -1 ) # If channel_id is -1, it is a dm message can_remove = False if auth_user['u_id'] == msg['author_id'] or is_dreams_owner( auth_user['u_id']): # If the user is the author of the message or a dreams owner, they can always edit it can_remove = True elif msg['channel_id'] != -1 and is_channel_owner( auth_user['u_id'], msg['channel_id']): # If the message was sent to a channel and the user is the channel owner can_remove = True if can_remove: author_id = msg['author_id'] # delete from messages cleared_messages = [ msg for msg in messages if msg['message_id'] != message_id ] messages.clear() for msg in cleared_messages: messages.append(msg) update_user_message_stats(author_id) update_message_stats() # delete from channel if is_channel_message: for channel in channels: if msg['message_id'] in channel['messages']: channel['messages'] = [ m for m in channel['messages'] if m != msg ] else: # delete from dm for dm in dms: if msg['message_id'] in dm['messages']: dm['messages'] = [ m for m in dm['messages'] if m != msg ] return {} else: raise AccessError( f"User {auth_user['handle_str']} does not have permission to remove message with id {message_id}" ) raise InputError(f"Message with id {message_id} no longer exists")
def message_send_v2(token, channel_id, message): ''' Summary: Send a message from authorised_user to the channel specified by channel_id. Note: Each message should have it's own unique ID. I.E. No messages should share an ID with another message, even if that other message is in a different channel. Args: token (string): A user session token channel_id (int): A channel_id number message (string): a message string Returns: empty dictionary which contains the message_id Raises: InputError when: Message is more than 1000 characters AccessError when: the authorised user has not joined the channel they are trying to post to ''' global users, channels, next_message_id, messages assert check_token(token) assert valid_channel(channel_id) auth_user = user_from_token(token) current_channel = find_channel( channel_id) #current channel with the given channel id if current_channel['standup']['is_active']: standup_send_v1(token, channel_id, message) return if len(message) > 1000: raise InputError(f"Message more than 1000 characters") if auth_user['u_id'] not in current_channel['all_members']: raise AccessError( f"the authorised user has not joined the channel they are trying to post to" ) current_time = current_unix_timestamp() #gives us the current timestamp current_message = {} current_message['message_id'] = next_message_id['id'] #message_id current_message['channel_id'] = channel_id current_message[ 'dm_id'] = -1 #this is a channel message not dm so for all dm's give -1 current_message['author_id'] = auth_user['u_id'] current_message['message'] = message current_message['reacts'] = [] current_message['time_created'] = current_time current_message['is_pinned'] = False current_channel['messages'].append(current_message['message_id']) next_message_id[ 'id'] += 1 #update the global message id so we get unique ids message_words = message.split(' ') # Standup start checker # Start a standup if message is exactly "/standup X" where X is a number of seconds if message.startswith('/standup') and len( message_words) == 2 and message_words[1].isdigit(): duration = int(message_words[1]) standup_start_v1(token, channel_id, duration) #notications checker and handler contains_tag = False tag_strings = [] if '@' in message: for word in message_words: if word.startswith('@'): tag_strings.append(word[1:]) contains_tag = True if contains_tag: for user_id in current_channel['all_members']: user = user_from_id(user_id) if user['handle_str'] in tag_strings: send_channel_tag_notification(auth_user['handle_str'], user_id, channel_id, message) messages.append(current_message) update_user_message_stats(auth_user['u_id']) update_message_stats() return { 'message_id': current_message['message_id'] #message_id }
def message_unreact_v1(token, message_id, react_id): """Summary Given a message within a channel or DM the authorised user is part of, remove a "react" to that particular message Args: token (string): A user session token message_id (int): A message id number react_id (int): the react type of this react, (only the 1 for now) Returns: Empty dictionary Raises: AccessError: The authorised user is not a member of the channel or DM that the message is within InputError: message_id is not a valid message within a channel or DM that the authorised user has joined react_id is not a valid React ID. The only valid react ID the frontend has is 1 Message with ID message_id does not contain an active React with ID react_id from the authorised user """ global messages check_token(token) auth_user = user_from_token(token) user_id = auth_user['u_id'] # react_id is not a valid React ID. The only valid react ID the frontend has is 1 if react_id != 1: raise InputError( f"react_id {react_id} is not a valid React ID. The only valid react ID the frontend has is 1" ) message_found = False for msg in messages: if msg['message_id'] == message_id: message_found = True if msg['channel_id'] != -1: if is_channel_member(user_id, msg['channel_id']) == False: raise AccessError( f"The authorised user {user_id} is not a member of the channel that the message is within" ) else: if is_dm_member(msg['dm_id'], user_id) == False: raise AccessError( f"The authorised user {user_id} is not a member of the dm that the message is within" ) # if 'reacts' in msg.keys(): react_peoples = msg['reacts'] check_react = False # check if this user has already reacted this message for react_people in react_peoples: if react_people == user_id: check_react = True msg['reacts'].remove(user_id) if check_react == False: raise InputError( f"Message {message_id} does not contain an active React with ID react_id from the authorised user" ) # else: # raise InputError(f"Message {message_id} does not contain an active React with ID react_id from the authorised user") # InputError: message_id is not a valid message within a channel or DM that the authorised user has joined if not message_found: raise InputError( f"message_id {message_id} is not a valid message within a channel or DM that the authorised user has joined" ) return {}
def message_react_v1(token, message_id, react_id): """Summary Given a message, update its text with new text. If the new message is an empty string, the message is deleted. Args: token (string): A user session token message_id (int): A message id number react_id (int): the react type of this react, (only the 1 for now) Returns: Empty dictionary Raises: AccessError: The authorised user is not a member of the channel or DM that the message is within InputError: message_id is not a valid message within a channel or DM that the authorised user has joined react_id is not a valid React ID. The only valid react ID the frontend has is 1 Message with ID message_id already contains an active React with ID react_id from the authorised user """ global messages check_token(token) auth_user = user_from_token(token) user_id = auth_user['u_id'] # react_id is not a valid React ID. The only valid react ID the frontend has is 1 if react_id != 1: raise InputError( f"react_id {react_id} is not a valid React ID. The only valid react ID the frontend has is 1" ) message_found = False for msg in messages: if msg['message_id'] == message_id: message_found = True if msg['channel_id'] != -1: if is_channel_member(user_id, msg['channel_id']) == False: raise AccessError( f"The authorised user {user_id} is not a member of the channel that the message is within" ) send_channel_message_react_notification( user_id, msg['author_id'], msg['channel_id']) if msg['dm_id'] != -1: if is_dm_member(msg['dm_id'], user_id) == False: raise AccessError( f"The authorised user {user_id} is not a member of the dm that the message is within" ) send_dm_message_react_notification(user_id, msg['author_id'], msg['dm_id']) # if 'reacts' in msg.keys(): react_peoples = msg['reacts'] check_react = False # check if this user has already reacted this message for react_people in react_peoples: if react_people == user_id: check_react = True raise InputError( f"Message {message_id} already contains an active React with ID react_id from the authorised user" ) if check_react == False: msg['reacts'].append(user_id) # else: # msg['reacts'] = [user_id] # InputError: message_id is not a valid message within a channel or DM that the authorised user has joined if not message_found: raise InputError( f"message_id {message_id} is not a valid message within a channel or DM that the authorised user has joined" ) return {}
def message_sendlaterdm_v1(token, dm_id, message, time_sent): """Summary Send a message from authorised_user to the dm specified by dm_id automatically at a specified time in the future Args: token (string): A user session token dm_id (int): A dm id number which the message will be sented to message (string): The text with which the user wishes to sent to dm time_sent (int): the time of when this message will be sent Returns: Dictionary: { message_id } the new message Raises: AccessError: the authorised user has not joined the dm they are trying to post to InputError: dm ID is not a valid dm Message is more than 1000 characters Time sent is a time in the past """ # current_time = current_unix_timestamp() # time_intervel = time_sent - current_time # # InputError: Time sent is a time in the past # if time_intervel < 0: # raise InputError("Time sent is a time in the past") # time.sleep(time_intervel) # message_id = message_senddm_v1(token, dm_id, message) # return { 'message_id': message_id } global users, dms, next_message_id, messages assert check_token(token) # InputError: dm ID is not a valid dm if valid_dm(dm_id) == False: raise InputError(f"DM ID {dm_id} is not a valid dm") # InputError: Message is more than 1000 characters if len(message) > 1000: raise InputError(f"Message {message} is more than 1000 characters") current_time = current_unix_timestamp() time_intervel = time_sent - current_time # InputError: Time sent is a time in the past if time_intervel < 0: raise InputError("Time sent is a time in the past") auth_user = user_from_token(token) current_dm = find_dm(dm_id) #current dm with the given dm id if auth_user['u_id'] not in current_dm['members']: raise AccessError( f"the authorised user has not joined the dm they are trying to post to" ) message_id = next_message_id['id'] next_message_id['id'] += 1 future_message = {} future_message['message_id'] = message_id #message_id future_message['dm_id'] = dm_id future_message[ 'channel_id'] = -1 #this is a dm message not dm so for all dm's give -1 future_message['author_id'] = auth_user['u_id'] future_message['message'] = message future_message['reacts'] = [] future_message['time_created'] = time_sent future_message['is_pinned'] = False future_messages.append(future_message) return { 'message_id': future_message['message_id'] #message_id }
def message_senddm_v1(token, dm_id, message): """Summary Send a message from authorised_user to the DM specified by dm_id. Note: Each message should have it's own unique ID. I.E. No messages should share an ID with another message, even if that other message is in a different channel or DM. Args: token (string): A user session token dm_id (int): A dream id number which the message will be sented to message (string): The text with which the user wishes to sent to dream Returns: Dictionary: { message_id } the new message Raises: AccessError: when: the authorised user is not a member of the DM they are trying to post to InputError: Message is more than 1000 characters """ global next_message_id check_token(token) auth_user = user_from_token(token) # InputError: Message is more than 1000 characters if len(message) > 1000: raise InputError(f"Message {message} is more than 1000 characters") for dm in dms: if dm['dm_id'] == dm_id: # AccessError: when: the authorised user is not a member of the DM they are trying to post to if auth_user['u_id'] not in dm['members']: raise AccessError( f"the authorised user is not a member of the DM {dm_id}") else: current_time = current_unix_timestamp() message_id = next_message_id['id'] next_message_id['id'] += 1 # create a new message which will be sent to the dm current_message = {} current_message['message_id'] = message_id current_message['channel_id'] = -1 current_message['dm_id'] = dm_id current_message['author_id'] = auth_user['u_id'] current_message['message'] = message current_message['reacts'] = [] current_message['time_created'] = current_time current_message['is_pinned'] = False contains_tag = False tag_strings = [] if '@' in message: for word in message.split(' '): if word.startswith('@'): tag_strings.append(word[1:]) contains_tag = True # the massage is already in the taget dm, send a notification if contains_tag: for user_id in dm['members']: user = user_from_id(user_id) if user['handle_str'] in tag_strings: send_dm_tag_notification(auth_user['handle_str'], user_id, dm_id, message) # add the new massage to the messages list in the data dm['messages'].append(message_id) messages.append(current_message) update_user_message_stats(auth_user['u_id']) update_message_stats() return {'message_id': message_id} raise InputError(f"DM id {dm_id} does not refer to a valid dm")
def channel_leave_v1(token, channel_id): ''' <Given a channel ID, the user removed as a member of this channel. Their messages should remain in the channel> Arguments: <token> (<string>) - <the user that is trying to leave from the channel> <channel_id> (<integer>) - <the channel that the user is trying to leave> Exceptions: InputError - Occurs when Channel ID is not a valid channel AccessError - Occurs when Authorised user is not a member of channel with channel_id Return Value: no returns in this function ''' check_token(token) # Channel ID is not a valid channel if not valid_channel(channel_id): raise InputError(f"Channel ID {channel_id} is not a valid channel") ''' user = user_from_token(token) u_id = user['u_id'] # Authorised user is not a member of channel with channel_id if not is_channel_member(u_id, channel_id): raise AccessError(f"Authorised user {u_id} is not a member of channel with channel_id") ''' auth_user = user_from_token(token) if is_channel_member(auth_user['u_id'], channel_id) == False: raise AccessError( f"Authorised user {auth_user['u_id']} is not a member of channel with channel_id" ) for channel in channels: if channel['channel_id'] == channel_id: channel['all_members'].remove(auth_user['u_id']) for owner_member in list(channel['owner_members']): if owner_member == auth_user['u_id']: channel['owner_members'].remove(auth_user['u_id']) auth_user['channels'].remove(channel_id) update_user_channel_stats(auth_user['u_id']) ''' # remove the member from the channel all_member list record = -1 for channel in channels: if channel['channel_id'] == channel_id: members = channel['all_members'] for i in range(len(members)): if members[i] == u_id: record = i if record != -1: members.remove(record) # remove this channel in the user's channels # record is the channel which need to be deleted record = -1 user_channel = user['channels'] for i in range(len(user_channel)): if user_channel[i] == channel_id: record = i # removed the record if record != -1: user_channel.remove(record) ''' return {}
def channel_messages_v2(token, channel_id, start): """Summary 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. Args: token (string): A user session token channel_id (int): A channel id number start (int): The index in the channel's list of messages at which to begin collecting them Returns: Dictionary: Contains the start index, the end index (-1 if the final message in the list was collected, or start + 50 if 50 messages were collected without reaching the end of the list) and the list of collected messages. Raises: AccessError: When an invalid token is given, or authorised user is not a member of channel with channel_id InputError: When channel_id does not refer to a valid channel or start is negative or greater than the total number of messages in the channel """ check_token(token) auth_user_id = user_from_token(token)['u_id'] # Retrieve messages from channel with given channel_id message_ids = None for channel in channels: if channel['channel_id'] == channel_id: if auth_user_id in channel['all_members']: message_ids = channel['messages'] else: raise AccessError( f"Authorised user is not a member of channel with id {channel_id}" ) if message_ids == None: # If list is still None, channel has not been found raise InputError( f"channel id {channel_id} does not refer to a valid channel") # Collect all messages in the target channel messageList = [] for message in messages: if message['message_id'] in message_ids: messageList.append(message) # If the channel has no messages and start = 0, return an empty list with end = -1 if start == 0 and not len(messageList): return {'messages': messageList, 'start': start, 'end': -1} # Check whether start index is negative if start < 0: raise InputError(f"start index cannot be negative (given {start})") messageList.reverse() # Most recent message should have index 0 # Collect up to 50 messages from the channel's messages if start < len( messageList ): # Check whether start index is within bounds of message list output = [] index = start while len(output) < 50: if index >= len( messageList ): # Index exceeds list size, max. number of messages have been collected return {'messages': output, 'start': start, 'end': -1} message = messageList[index] react_dict = { 'react_id': 1, 'u_ids': message['reacts'], 'is_this_user_reacted': auth_user_id in message['reacts'] } message_dict = {} message_dict['message_id'] = message['message_id'] message_dict['u_id'] = message['author_id'] message_dict['message'] = message['message'] message_dict['time_created'] = message['time_created'] message_dict['reacts'] = [react_dict] message_dict['is_pinned'] = message['is_pinned'] output.append(message_dict) index += 1 # If loop finishes, 50 messages have been collected # Set end to -1 if channel had exactly 50 messages from start index, or start + 50 otherwise return { 'messages': output, 'start': start, 'end': start + 50 if len(messageList) > start + 50 else -1 } else: raise InputError( f"start index {start} is greater than the total number of messages in the channel {len(messages)}" )