def sethandle(token, handle_str): """ INPUTS: token, handle_str RETURNS: {} INPUTERROR: *2 <= handle_str <= 20 * handle already used DESCRIPTION: Updates an AUTHORISED user's handle """ global user_data if len(handle_str) >= 20 or len(handle_str) <= 1: raise InputError( description='Handle string must be between 1 and 20 characters') if aux_discrete.check_handle_in_use is False: raise InputError(description='Handle string is already in use') if aux_common.decode_token(token) == {'u_id': 'error_invalid_token'}: raise AccessError(description="Invalid token") user_id = aux_common.decode_token(token) for user in user_data: if user['u_id'] == user_id['u_id']: user['handle'] = handle_str return {}
def setemail(token, email): """ INPUTS: token, email RETURNS: {} INPUTERROR: *Email not valid (Copy code) *Already in use DESCRIPTION: Updates authorised user's email address """ global user_data if not aux_discrete.check_email_valid(email): raise InputError(description='Email is not of valid form') if not aux_discrete.check_email_in_use(email): raise InputError(description='Email already in use') if aux_common.decode_token(token) == {'u_id': 'error_invalid_token'}: raise AccessError(description="Invalid token") user_id = aux_common.decode_token(token) for user in user_data: if user['u_id'] == user_id['u_id']: user['email'] = email return {}
def send_later(token, channel_id, message, time_to_send): ''' ## DESCRIPTION ## Given a token, channel_id, and message, decodes the token, and adds 'message' into the pending messages database with the requested time_to_send instead of datetime.utcnow() ## TYPES ## token - string channel_id - integer message - string time_to_send - datetime ## RETURN VALUE ## {} ## EXCEPTIONS ## InputError if - Message is more than 1000 characters - Message is 0 characters. - The time_to_send is in the past. AccessError if - Token is invalid - The user has not joined the channel they are trying to post to ''' global pending_message_data if len(message) <= 0 or len(message) > 1000: raise InputError( description='Message must be between 1 and 1000 characters long.') if time_to_send < datetime.utcnow(): raise InputError(description='The date to send must be in the future.') user = aux_common.decode_token(token) if user['u_id'] == 'error_invalid_token' or not isinstance(user['u_id'], int) or \ not aux_common.user_in_channel(user['u_id'], channel_id): raise AccessError( description='User is not in the channel that message is being sent to') message_dict = { 'temporary_message_id': len(pending_message_data), 'message': message, 'sending_u_id': user['u_id'], 'timestamp': time_to_send, 'channel_id': int(channel_id), 'reacting_u_ids': [], 'pinned': False } print(time_to_send, flush=True) pending_message_data.append(message_dict) return {'message_id': (len(message_data) - 1)}
def active(token, channel_id): ''' Requirements: correct channel_id. Checks whether a standup with channel_id exists in standup_list. It will simply retrieve from data.py and return it in a neat package. #Raises 1 possible InputError ''' # check for valid token common.token_valid(token) # check for valid channel if common.ch_id_exists(channel_id) == -1: raise InputError('This channel does not exist.') is_active = False time_finish = None for channel in data.standup_list: if channel["channel_id"] == channel_id: is_active = True time_finish = int(channel["time_finish"].replace( tzinfo=datetime.timezone.utc).timestamp()) return {'is_active': is_active, 'time_finish': time_finish}
def profile(token, u_id): """ INPUTS: token, u_id DESCRIPTION: For a valid user, Returns information about their user id, email first name last name and handle RETURNS: { user } INPUTERROR: * User with u_id is not a valid user """ if aux_common.decode_token(token) == {'u_id': 'error_invalid_token'}: raise AccessError(description="Invalid token") # Input Error if not aux_discrete.user_exists(u_id): raise InputError(description=f'User with u_id {u_id} is not valid') # Loop through all the users and return the right user through its identifiers (u_id) for user in user_data: if user['u_id'] == u_id: # check if i have the layout of the dictionary plss return { 'user': { 'u_id': user['u_id'], 'email': user['email'], 'name_first': user['first_name'], 'name_last': user['last_name'], 'handle_str': user['handle'], 'profile_img_url': user['profile_img_url'] } # Need to add handle_str to the global data } return {}
def start(token, channel_id, length): ''' Requirements: correct channel_id and token This will start a standup session for 'length' seconds. Messages sent to this session must be through 'send'. A message sent will be appended to the final message using send. At time = length, the message package will be sent over to message_data. #Raises 2 possible InputErrors ''' # preliminary checks if common.ch_id_exists(channel_id) == -1: raise InputError('Invalid Channel. Cannot start standup.') if active(token, channel_id)['is_active']: raise InputError( 'A standup is active. Only 1 standup may be running at a time.') authorized_caller = common.decode_token(token) if authorized_caller["u_id"] == { "u_id": "error_invalid_token" } or not common.user_in_channel(authorized_caller["u_id"], channel_id): raise AccessError('You do not have permission to do this.') # check if length is valid. Assuming that a standup lasts for at least 1 second. if length < 1: raise InputError( description= 'You cannot start a standup for a time less than 1 second.') # keep start and finish for better accuracy time_start = datetime.datetime.utcnow() time_finish = time_start + datetime.timedelta(seconds=length) discrete.standup_data(time_start, time_finish, channel_id, authorized_caller['u_id']) index = discrete.find_standup(channel_id) # at length = length seconds, sends all the messages from the queue as a complete package Timer(length, discrete.send_standup, [token, channel_id, index]).start() Timer(length + 1, data.standup_list.pop, [index]).start() return { 'time_finish': int(time_finish.replace(tzinfo=datetime.timezone.utc).timestamp()) }
def unpin(token, message_id): ''' ## DESCRIPTION ## Given a token and message_id, changes the 'pinned' attribute of the corresponding 'message_id' dictinoary to False. ## TYPES ## token - string message_id - integer ## RETURN VALUE ## {} ## EXCEPTIONS ## InputError if - message_id is invalid - the user is not an owner - message is not pinned AccessError if - The user is not a member of the channel that the message is within - Token is invalid ''' global message_data message_index = aux_discrete.find_message(message_id) if message_index == -1: raise InputError(description='Message ID is invalid.') user = aux_common.decode_token(token) if user['u_id'] == 'error_invalid_token' or not isinstance(user['u_id'], int): raise AccessError(description="Invalid token") if not aux_common.user_in_channel(user['u_id'], message_data[message_index]['channel_id']): raise InputError( description="User is not in the channel that the message to unpin to is in.") if not message_data[message_index]['pinned']: raise InputError(description="Message not yet pinned") if not aux_common.user_is_owner_of_slackr(user['u_id']) and not \ aux_common.user_is_owner_channel(user['u_id'], message_data[message_index]['channel_id']): raise InputError( description="User is not owner of the slackr and not owner of the channel") message_data[message_index]['pinned'] = False return {}
def edit(token, message_id, message): ''' ## DESCRIPTION ## Given a token and message_id, if the user has requisite permissions, changes the message to the new message. ## TYPES ## token - string message_id - integer message - string ## RETURN VALUE ## {} ## EXCEPTIONS ## InputError if - message_id is invalid - new message is empty or greater than 1000 characters AccessError if: - Token is invalid AccessError if ALL OF THE FOLLOWING: - User is not the authorised user (the creator of message) - User is not an owner of this channel - User is not an owner of the slackr ''' global message_data if len(message) <= 0 or len(message) > 1000: raise InputError( description="Message must be between 0 and 1000 characters long") msg_index = aux_discrete.find_message(message_id) if msg_index == -1: raise InputError(description='Message ID is invalid.') user = aux_common.decode_token(token) if user['u_id'] == 'error_invalid_token' or not isinstance(user['u_id'], int): raise AccessError(description="Invalid token") if not aux_common.user_is_owner_channel(user['u_id'], message_data[msg_index]['channel_id']) \ and not aux_common.user_is_owner_of_slackr(user['u_id']) \ and message_data[msg_index]['sending_u_id'] != user['u_id']: raise AccessError( description="User does not have requisite permission to remove the message") message_data[msg_index]['message'] = message return {}
def login(email, password): ''' ## DESCRIPTION ## Given a registered users' email and password and generates a valid token for the user to remain authenticated ## TYPES ## email - string password - string ## RETURN VALUE ## {u_id, token} ## EXCEPTIONS ## InputError if - Invalid email - Email entered does not belong to a user - Incorrect password ''' global user_data registered_email = False if not aux_discrete.check_email_valid(email): raise InputError(description="Email entered is not a valid email") for user in user_data: if user["email"] == email: registered_email = True if not registered_email: raise InputError(description="Email entered does not belong to a user") for user in user_data: if user["email"] == email and user["password"] != password: raise InputError(description="Password is not correct") u_id = 0 for user in user_data: if user["email"] == email: user["is_logged_in"] = True u_id = user["u_id"] return {'u_id': u_id, 'token': aux_common.encode_token({'u_id': u_id})}
def passwordreset_reset(reset_code, new_password): try: reset_code = int(reset_code) except ValueError: raise InputError(description="Invalid reset code.") u_index = 0 for user in user_data: curr_code = user.get('reset_code') if curr_code == reset_code: break u_index += 1 if u_index >= len(user_data): raise InputError(description="Invalid reset code.") user_data[u_index]['password'] = new_password user_data[u_index].pop('reset_code') return {}
def react(token, message_id, react_id): ''' ## DESCRIPTION ## Given a token, message_id, and react_id, adds the 'token' user to the 'reacting_u_ids' list of the corresponding 'message_id'. ## TYPES ## token - string message_id - integer react_id - integer ## RETURN VALUE ## {} ## EXCEPTIONS ## InputError if - react_id is not 1 - message_id is not a valid message within a channel the authorised user has joined - User has already 'reacted' to this message. AccessError if - Token is invalid ''' global message_data message_index = aux_discrete.find_message(message_id) if message_index == -1: raise InputError(description='Message ID is invalid.') user = aux_common.decode_token(token) if user['u_id'] == 'error_invalid_token' or not isinstance(user['u_id'], int): raise AccessError(description="Invalid token") if not aux_common.user_in_channel(user['u_id'], message_data[message_index]['channel_id']): raise InputError( description="User is not in the channel that the message to react to is in.") if react_id != 1: raise InputError(description="Invalid react ID") if user['u_id'] in message_data[message_index]['reacting_u_ids']: raise InputError(description="Message already reacted to") message_data[message_index]['reacting_u_ids'].append(user['u_id']) return {}
def send(token, channel_id, message): ''' ## DESCRIPTION ## Given a token, channel_id, and message, decodes the token, and adds the message 'message' into the messages database with the user of the token. ## TYPES ## token - string channel_id - integer message - string ## RETURN VALUE ## {} ## EXCEPTIONS ## InputError if - Message is more than 1000 characters - Message is 0 characters. AccessError - Token is invalid - The user has not joined the channel they are trying to post to ''' global message_data, next_id if len(message) <= 0 or len(message) > 1000: raise InputError( description="Message must be between 1 and 1000 characters long.") user = aux_common.decode_token(token) if user['u_id'] == 'error_invalid_token' or not isinstance(user['u_id'], int): raise AccessError(description='Invalid token') if not aux_common.user_in_channel(user['u_id'], channel_id): raise AccessError( description='User is not in the channel that message is being sent to') next_msg_id = next_id.get('message_id') if not next_msg_id: # key does not exist yet next_id['message_id'] = 0 next_msg_id = 0 next_id['message_id'] += 1 message_dict = { 'message_id': next_msg_id, 'message': message, 'sending_u_id': user['u_id'], 'timestamp': datetime.utcnow(), 'channel_id': int(channel_id), 'reacting_u_ids': [], 'pinned': False } message_data.append(message_dict) return {'message_id': len(message_data) - 1}
def setname(token, first_name, last_name): """ INPUTS:token, name_first,name_last RETURNS: {} INPUTERROR: * Name_first is not between 1 and 50 charactors inclusive in length * Name_last is not between 1 and 50 characters inclusive in length DESCRIPTION: Updates the authorised user's first and last name """ global user_data # Raise errors first if len(first_name) >= 50 or len(first_name) <= 1: raise InputError( description='Length of first name must be between 1 - 50 characters' ) if len(last_name) >= 50 or len(last_name) <= 1: raise InputError( description='Length of last name must be between 1 - 50 characters' ) # decode token to get user_id, assuming u_id is the identifier for the user_data if aux_common.decode_token(token) == {'u_id': 'error_invalid_token'}: raise AccessError(description="Invalid token") # Reveals dictionary of form {"u_id": u_id} user_id = aux_common.decode_token(token) # remember user_id is a dictionary of 'u_id' # Go into that users dictionary and change the first_name and last_name to inputs for user in user_data: if user['u_id'] == user_id['u_id']: user['first_name'] = first_name user['last_name'] = last_name return {}
def send(token, channel_id, message): ''' Requirements: correct token, channel_id and len(message) <= 1000 Appends a message to the standup_messages list. It will depend on an aux_ function buffering it first before appending at the end of the list. #Raises 3 Possible InputErrors, 1 Access Error ''' # ch_id is invalid -> InputError index = common.ch_id_exists(channel_id) if index == -1: raise InputError('Channel does not exist.') # message > 1000 characters if len(message) > 1000 or len(message) == 0: raise InputError( description= f'Your message is {len(message)-1000} over the limit. Your message cannot be over 1000 \ characters') # user is not authorized -> AccessError authorized_caller = common.decode_token(token) if authorized_caller == {"u_id": "error_invalid_token"} or \ not common.user_in_channel(authorized_caller["u_id"], channel_id): raise AccessError( 'You cannot send a standup message in this channel which you are not part of.' ) # active standup false -> InputError if not active(token, channel_id)['is_active']: raise InputError('Standup not active.') index = discrete.find_standup(channel_id) u_index = discrete.find_user(authorized_caller['u_id']) data.standup_list[index][ 'message'] += f"\n{data.user_data[u_index]['handle']}: {message}" print(f"message now {data.standup_list[index]['message']}", flush=True) return {}
def create(token, name, is_public): ''' ## DESCRIPTION ## Given a token, name, and is_public boolean, creates a new channel with the properties passed. ## TYPES ## token - string name - string is_public - boolean ## RETURN VALUE ## { channel_id } ## EXCEPTIONS ## AccessError if: - Token is invalid InputError if: - The name is < 1 or > 20 characters long ''' global all_channels, next_id user = aux_common.decode_token(token) if user['u_id'] == 'error_invalid_token' or not isinstance( user['u_id'], int): raise AccessError(description="Invalid token") if len(name) < 1 or len(name) > 20: raise InputError( description="Name of channel must be 1 to 20 characters long.") next_ch_id = next_id.get('channel_id') if not next_ch_id: # key does not exist yet next_id['channel_id'] = 0 next_ch_id = 0 next_id['channel_id'] += 1 new_channel_dict = { "name": name, "channel_id": next_ch_id, "owner_members": [user['u_id']], "all_members": [user['u_id']], "is_public": is_public } all_channels.append(new_channel_dict) return {'channel_id': next_ch_id}
def remove(token, message_id): ''' ## DESCRIPTION ## Given a token and message_id, if the user has requisite permissions, removes the message message_id ## TYPES ## token - string message_id - integer ## RETURN VALUE ## {} ## EXCEPTIONS ## InputError if - message_id is invalid AccessError if: - Token is invalid AccessError if ALL OF THE FOLLOWING: - User is not the authorised user (the creator of message) - User is not an owner of this channel - User is not an owner of the slackr ''' global message_data msg_index = aux_discrete.find_message(message_id) if msg_index == -1: raise InputError(description='Message ID is invalid.') user = aux_common.decode_token(token) if user['u_id'] == 'error_invalid_token' or not isinstance(user['u_id'], int): raise AccessError(description="Invalid token") if not aux_common.user_is_owner_channel(user['u_id'], message_data[msg_index]['channel_id']) \ and not aux_common.user_is_owner_of_slackr(user['u_id']) and \ message_data[msg_index]['sending_u_id'] != user['u_id']: raise AccessError( description="User does not have requisite permission to remove the message") message_data.pop(msg_index) return {}
def echo(): data = request.args.get('data') if data == 'echo': raise InputError(description='Cannot echo "echo"') return dumps({'data': data})
def upload_photo(token, img_url, x_start, y_start, x_end, y_end): ''' INPUTS: token (str), img_url (str), x_start, y_start, x_end, y_end (ints) RETURNS: {} AUTHERROR: invalid token INPUTERROR: Image URL request raises status code not 200 x_start, y_start, x_end, y_end are not in the dimensions of the image at the URL or are invalid Image URL is not linked to a valid image Image URL is not a JPEG/JPG file. DESCRIPTION: Updates an AUTHORISED user's handle ''' global user_data, next_id try: x_start = int(x_start) y_start = int(y_start) x_end = int(x_end) y_end = int(y_end) except ValueError: raise InputError(description="x & y values must be integers") u_id = aux_common.decode_token(token)['u_id'] if u_id == "error_invalid_token": raise AccessError(description="Invalid token") response = requests.get(img_url) if response.status_code != 200: raise InputError( description=f"Image URL invalid: status code {response.status_code}" ) try: img = Image.open(BytesIO(response.content)) except UnidentifiedImageError: raise InputError( description="That URL is not a direct link to an image") if img.format != "JPEG": raise InputError(description="Image must be a JPEG/JPG") width, height = img.size if x_start > width or x_end > width or y_start > height or y_end > height: raise InputError( description= "One or more crop parameters were out of the photo's range.") try: img = img.crop((x_start, y_start, x_end, y_end)) except SystemError: raise InputError( description= "Crop parameters were invalid. Are the ends greater than the starts?" ) next_img_name = next_id.get('image_name') if not next_img_name: # key does not exist yet next_id['image_name'] = 0 next_img_name = 0 try: img.save(f"./profile_images/{next_img_name}.jpg", "JPEG") except SystemError: raise InputError( description= "Error occurred while saving the image. Are the ends greater than the starts?" ) u_index = aux_discrete.find_user(u_id) user_data[u_index][ 'profile_img_url'] = f"http://127.0.0.1:8080/profileimages/{next_img_name}.jpg" next_id['image_name'] += 1 return {}
def register(email, password, name_first, name_last): ''' ## DESCRIPTION ## Given a email, password, name_first and name_last, create a new account for them and return a new token for authentication in their session. A handle is generated that is the concatentation of a lowercase-only first name and last name. If the concatenation is longer than 20 characters, it is cutoff at 20 characters. If the handle is already taken, you may modify the handle in any way you see fit to make it unique. ## TYPES ## email - string password - string name_first - string name_last - string ## RETURN VALUE ## {u_id, token} ## EXCEPTIONS ## InputError if - Invalid email - Email is already being used - Password is less than 6 characters long - name_first not is between 1 and 50 characters inclusive in length - name_last not is between 1 and 50 characters inclusive in length ''' global user_data, next_id if len(password) <= 5: raise InputError( description="Password entered is less than 6 characters long") if len(name_first) <= 0 or len(name_first) > 50: raise InputError( description="First name must be between 1 and 50 characters long.") if len(name_last) <= 0 or len(name_last) > 50: raise InputError( description="Last name must be between 1 and 50 characters long.") if not aux_discrete.check_email_valid(email): raise InputError(description="Email entered is not a valid email") if not aux_discrete.check_email_in_use(email): raise InputError( description="Email address is already being used by another user") next_u_id = next_id.get('u_id') if not next_u_id: # key does not exist yet next_id['u_id'] = 0 next_u_id = 0 next_id['u_id'] += 1 new_user_dict = { "u_id": next_u_id, "email": email, "first_name": name_first, "last_name": name_last, "password": password, "handle": aux_discrete.new_handle(name_first, name_last), "is_owner_of_slackr": True if len(user_data) == 0 else False, "is_logged_in": True, "profile_img_url": "http://127.0.0.1:8080/profileimages/default.jpg" } user_data.append(new_user_dict) return {'u_id': next_u_id, 'token': aux_common.encode_token({'u_id': next_u_id})}
def passwordreset_request(email): ''' ## DESCRIPTION ## Given an email, sends an email with a secret code, which, when entered in passwordreset/reset, shows the user trying to reset the password is the one who got the email. The secret code is generated by a random number generator, which is then stored in the corresponding user's dictionary in user_data. ## TYPES ## email - string ## RETURN VALUE ## {} ## EXCEPTIONS ## InputError if - Email provided is not associated with a user - Could not connect to SMTP server ''' user_index = 0 for user in user_data: if user["email"] == email: break user_index += 1 if user_index >= len(user_data): raise InputError(description="That email is not associated with a user.") # Cryptographically secure random 8 digit number code = random.SystemRandom().randint(10000000, 99999999) # Set reset code in database user_data[user_index]['reset_code'] = code # Variables setup message = MIMEMultipart("alternative") message["Subject"] = "Your password reset code for Slackr" message["From"] = "*****@*****.**" message["To"] = email message_plain = f"""\ Hi, {user_data[user_index]['first_name']}! Someone (hopefully you) requested a password reset for the account associated with this email. Slackr user handle: {user_data[user_index]['handle']} The code to reset the password is {code}. Please enter this code on the password reset page. If you did not request this change, you do not need to do anything. Regards, goodmemes Slackr team. """ message_html = f"""\ <html> <body> <p> Hi, {user_data[user_index]['first_name']}! </p> <p> Someone (hopefully you) requested a password reset for the account associated with this email. </p> <p> Slackr user handle: {user_data[user_index]['handle']} </p> <p> <b> The code to reset the password is {code}.</b> </p> <p> Please enter this code on the password reset page. </p> <p> If you did not request this change, you do not need to do anything. </p> <p> Regards, <br> goodmemes Slackr team.</p> </body> </html> """ message.attach(MIMEText(message_plain, "plain")) message.attach(MIMEText(message_html, "html")) ctxt = ssl.create_default_context() try: with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=ctxt) as server: server.login("*****@*****.**", "kLXpDYUbo87B") server.sendmail("*****@*****.**", email, message.as_string()) except: raise InputError("Error occurred while sending reset email.") return {}
def guess(token, channel_id, X): ''' Input - valid token, channel ID and a guess X. Behavior: Given a message with some 2nd string 'X', determines whether the guess is correct or not. Output - {} ''' # ch_id is invalid -> InputError index = common.ch_id_exists(channel_id) if index == -1: raise InputError( description='The channel you are guessing on does not exist.') # check if token is valid authorized_caller = common.decode_token(token) if authorized_caller == {"u_id": "error_invalid_token"} or \ not common.user_in_channel(authorized_caller["u_id"], channel_id): raise AccessError( description='You do not have permission to do this.') # ensure channel has a hangman session # NOTE YOU must abstract these functions ASAP. index = None for session in hangman_sessions: if session["channel_id"] == channel_id: index = hangman_sessions.index(session) if index == None: raise InputError(description='A hangman session is not running right now') X = X[0].split(' ') # if word not specified (is list), generate a random word if len(X) != 1: X = X[1] # check if X has been guessed already if X in hangman_sessions[index]['guesses']: raise InputError(description='Your guess has already been guessed.') # catch empty input error if not isinstance(X, str): raise InputError(description='Usage: /guess [letter]') # SEND THE /guess X as a message to message data. message.send(token, channel_id, '/guess ' + X) # check if the guess results in the complete answer hm_word = hangman_sessions[index]['word'] guesses = hangman_sessions[index]['guesses'] guesses.append(X) prev_word = hangman_sessions[index]['decrement_word'] hangman_sessions[index]['decrement_word'] = hangman_sessions[index]['decrement_word'].replace(X, '') word_decrement = hangman_sessions[index]['decrement_word'] if prev_word == word_decrement: hangman_sessions[index]["bad_attempts"] += 1 # render ASCII or some image art. bot_message = _render_ascii( hangman_sessions[index]["bad_attempts"], channel_id, guesses, hm_word) if hm_word.replace(X, '') == hm_word: hangman_sessions[index]['remaining_guesses'] -= 1 # if guess results in complete answer, then done and send congratulations if word_decrement == '': hangman_sessions.pop(index) bot_message += "\n\(^O^)/\nYou've correctly guessed the word!" # decrement the amount of guesses available, and if no more guesses, # remove hangman session and send taunting message else: if hangman_sessions[index]['remaining_guesses'] == 0: hangman_sessions.pop(index) bot_message += f"\n(✖╭╮✖)\nThe word is {hm_word}, you have lost\nF to pay respects" else: bot_message += "\nLetters guessed:" for word in hangman_sessions[index]['guesses']: bot_message += f" {word}" # send message as the bot bot_send(bot_message, channel_id) return {}
def start(token, channel_id, word): ''' Input - valid token, channel_id and word. Word validity is handled in the frontend. Behavior: Starts a hangman session in a channel. Takes a word from the random_word mod, and parses it so that it only contains alphabetical characters. All hangman sessions are stored in a hangman data structure. Output - {} ''' # ch_id is invalid -> InputError index = common.ch_id_exists(channel_id) if index == -1: raise InputError( description='The channel you are attempting to start a hangman in does not exist.') # check if token is valid authorized_caller = common.decode_token(token) if authorized_caller == {"u_id": "error_invalid_token"} or \ not common.user_in_channel(authorized_caller["u_id"], channel_id): raise AccessError( description='You do not have permission to start hangman.') # check if a hangman session is ongoing for session in hangman_sessions: if session["channel_id"] == channel_id: raise InputError( description='A hangman session is already ongoing in this channel') final_word = word[0].split(' ') # if word not specified (is list), generate a random word if len(final_word) == 1: _site = "http://svnweb.freebsd.org/csrg/share/dict/words?view=co&content-type=text/plain" response = requests.get(_site) _words = response.content.splitlines() final_word = random.choice(_words).decode() else: final_word = final_word[1].strip() # add to hangman sessions hangman_sessions.append({ "channel_id": channel_id, "word": final_word.lower(), "decrement_word": final_word.lower(), "guesses": [], "remaining_guesses": 8, "bad_attempts": 0 }) # send message as the bot idX = discrete.find_user(authorized_caller["u_id"]) starter_name = user_data[idX]["first_name"] bot_message = f"{starter_name} has just started a hangman!" bot_message += _missing([], final_word) bot_send(bot_message, channel_id) return {}