示例#1
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}
示例#2
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)}
示例#3
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 {}
示例#4
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())
    }
示例#5
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 {}
示例#6
0
def search(token, query_str):
    '''
    ## DESCRIPTION ##
    Given a query string, return a collection of messages in all of the channels
    that the user has joined that match the query. Results are sorted from most
    recent message to least recent message.

    ## TYPES ##
    token - string
    query_str - string

    ## RETURN VALUE ##
    {messages}

    ## EXCEPTIONS ##
    N/A
    '''

    message_list = []

    user_id = aux_common.decode_token(token)

    if user_id == {'u_id': "error_invalid_token"}:
        raise AccessError(description="Invalid Token")

    for message in message_data:
        if aux_common.user_in_channel(user_id['u_id'], message['channel_id']):
            if query_str in message['message']:
                message_dict = {
                    "message_id": message['message_id'],
                    "u_id": message['sending_u_id'],
                    "message": message['message'],
                    "time_created": message['timestamp'].replace(tzinfo=datetime.timezone.utc).\
                        timestamp(),
                    "reacts": [{'react_id': 1,
                                'u_ids': message['reacting_u_ids'],
                                'is_this_user_reacted': \
                                    bool(user_id['u_id'] in message['reacting_u_ids'])}],
                    "is_pinned": message['pinned'],
                }
                message_list.append(message_dict)

    #print(f"returning {'messages': message_dict}", flush=True)
    return{'messages': message_list}
示例#7
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 {}
示例#8
0
def list_(token):
    '''
    ## DESCRIPTION ##
    Given a token, returns the channels the user is in.

    ## TYPES ##
    token - string

    ## RETURN VALUE ##
    {'channels': [
        {
            'channel_id': <integer>,
            'name': string
        },
        ...
    ]}

    ## EXCEPTIONS ##
    AccessError if:
        - Token is invalid
    '''
    global all_channels
    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")

    channels_dict = {'channels': []}
    for channel in all_channels:
        if aux_common.user_in_channel(user['u_id'], channel['channel_id']):
            channels_dict['channels'].append({
                'channel_id':
                channel['channel_id'],
                'name':
                channel['name']
            })

    return channels_dict
示例#9
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 {}
示例#10
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 {}