예제 #1
0
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 {}
예제 #2
0
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 {}
예제 #3
0
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)}
예제 #4
0
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}
예제 #5
0
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 {}
예제 #6
0
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())
    }
예제 #7
0
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 {}
예제 #8
0
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 {}
예제 #9
0
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})}
예제 #10
0
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 {}
예제 #11
0
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 {}
예제 #12
0
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}
예제 #13
0
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 {}
예제 #14
0
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 {}
예제 #15
0
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}
예제 #16
0
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 {}
예제 #17
0
def echo():
    data = request.args.get('data')
    if data == 'echo':
        raise InputError(description='Cannot echo "echo"')
    return dumps({'data': data})
예제 #18
0
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 {}
예제 #19
0
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})}
예제 #20
0
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 {}
예제 #21
0
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 {}
예제 #22
0
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 {}