Example #1
0
def store_data(info):
    """Store validated data in the database."""
    # Check if user exists
    team = Teams.query.get(info['team_id'])

    if team is None:
        # Create new team
        new_team = Teams(info['team_id'])
        new_team.token = info['token']
        new_team.bot_id = info['bot_id']
        new_team.bot_token = info['bot_token']

        # Store new user
        report_event('team_added', info)
        DB.session.add(new_team)

    else:
        # Update team info
        team.token = info['token']
        team.bot_id = info['bot_id']
        team.bot_token = info['bot_token']
        report_event('team_updated', info)

    # Update DB
    DB.session.commit()

    return
Example #2
0
def store_data(info):
    """Store validated data in the database."""
    # Check if user exists
    team = Teams.query.get(info['team_id'])

    if team is None:
        # Create new team
        new_team = Teams(team_id=info['team_id'],
                         token=info['token'],
                         bot_id=info['bot_id'],
                         bot_token=info['bot_token'])

        # Store new user
        report_event('team_added', info)
        DB.session.add(new_team)

    else:
        # Update team info
        team.token = info['token']
        team.bot_id = info['bot_id']
        team.bot_token = info['bot_token']
        report_event('team_updated', info)

    # Update DB
    DB.session.commit()
Example #3
0
def is_valid_token(token):
    """Check that the team has a valid token."""
    # Set auth object
    auth = Auth(token)

    try:
        # Make request
        result = auth.test()

    except Error as err:
        # Check for auth errors
        report_event(str(err), {
            'token': token
        })
        return False

    # Check for further errors
    if not result.successful:
        report_event('token_invalid', {
            'token': token,
            'result': result.__dict__
        })
        return False

    # Return successful
    return True
Example #4
0
def send_roll(team, roll, args):
    """Post the roll to Slack."""
    # Set up chat object
    chat = Chat(team.bot_token)

    try:
        # Attempt to post message
        chat.post_message(args['channel_id'],
                          roll,
                          username='******',
                          icon_emoji=':game_die:')

    except Error as err:
        report_event(str(err), {
            'team': team.__dict__,
            'roll': roll,
            'args': args
        })

        # Check specifically for channel errors
        if str(err) == 'channel_not_found':
            err_msg = "{0} is not authorized to post in this channel.".format(
                'The {0} bot'.format(PROJECT_INFO['name_full']))
            err_msg += ' Please invite it to join this channel and try again.'
            return err_msg

        # Report any other errors
        return '{0} encountered an error: {1}'.format(
            PROJECT_INFO['name_full'], str(err))

    # Return no errors
    return None
Example #5
0
def get_token(code):
    """Request a token from the Slack API."""
    # Set OAuth access object
    oauth = OAuth()

    try:
        # Attempt to make request
        result = oauth.access(client_id=PROJECT_INFO['client_id'],
                              client_secret=PROJECT_INFO['client_secret'],
                              redirect_uri=PROJECT_INFO['valid_url'],
                              code=code)

    except Error as err:
        report_event('oauth_error', {'code': code, 'error': str(err)})
        abort(400)

    if not result.successful:
        report_event('oauth_unsuccessful', {
            'code': code,
            'result': result.__dict__
        })
        abort(400)

    # Setup return info
    info = {
        'token': result.body['access_token'],
        'team_id': result.body['team_id'],
        'bot_id': result.body['bot']['bot_user_id'],
        'bot_token': result.body['bot']['bot_access_token']
    }

    # Return info
    return info
Example #6
0
def home():
    """Render app homepage template."""
    if request.method == 'POST':
        report_event('post_request', request.form.to_dict())
        return roll.make_roll(request.form.to_dict())

    else:
        return render_template('index.html',
                               project=PROJECT_INFO,
                               allowed_commands=ALLOWED_COMMANDS)
Example #7
0
def home():
    """Render app homepage template."""
    if request.method == 'POST':
        report_event('post_request', request.form.to_dict())
        return roll.make_roll(request.form.to_dict())

    else:
        return render_template(
            'index.html',
            project=PROJECT_INFO,
            allowed_commands=ALLOWED_COMMANDS
        )
Example #8
0
def validate_return(args):
    """Run data validation functions."""
    # Make sure we have args
    if not args['state'] or not args['code']:
        report_event('missing_args', args)
        abort(400)

    # Validate state
    validate_state(args['state'])

    # Get access token and info
    token_info = get_token(args['code'])

    # Set up storage methods
    store_data(token_info)

    # Set success url
    redirect_url = '{0}?success=1'.format(PROJECT_INFO['base_url'])

    # Return successful
    return redirect_url
Example #9
0
def validate_return(args):
    """Wrapper function for data validation functions."""
    # Make sure we have args
    if not args['state'] or not args['code']:
        report_event('missing_args', args)
        abort(400)

    # Validate state
    validate_state(args['state'])

    # Get access token and info
    token_info = get_token(args['code'])

    # Set up storage methods
    store_data(token_info)

    # Set success url
    redirect_url = '{0}?success=1'.format(PROJECT_INFO['base_url'])

    # Return successful
    return redirect_url
Example #10
0
def validate_state(state):
    """Validate state token returned by authentication."""
    try:
        # Attempt to decode state
        state_token = GENERATOR.loads(
            state, max_age=timedelta(minutes=60).total_seconds())

    except SignatureExpired:
        # Token has expired
        report_event('token_expired', {'state': state})
        abort(400)

    except BadSignature:
        # Token is not authorized
        report_event('token_not_authorized', {'state': state})
        abort(401)

    if state_token != PROJECT_INFO['client_id']:
        # Token is not authorized
        report_event('token_not_valid', {
            'state': state,
            'state_token': state_token
        })
        abort(401)

    # Return success
    return
Example #11
0
def validate_state(state):
    """Validate state token returned by authentication."""
    try:
        # Attempt to decode state
        state_token = GENERATOR.loads(
            state,
            max_age=timedelta(minutes=60).total_seconds()
        )

    except SignatureExpired:
        # Token has expired
        report_event('token_expired', {
            'state': state
        })
        abort(400)

    except BadSignature:
        # Token is not authorized
        report_event('token_not_authorized', {
            'state': state
        })
        abort(401)

    if state_token != PROJECT_INFO['client_id']:
        # Token is not authorized
        report_event('token_not_valid', {
            'state': state,
            'state_token': state_token
        })
        abort(401)

    # Return success
    return
Example #12
0
def get_token(code):
    """Request a token from the Slack API."""
    # Set OAuth access object
    oauth = OAuth()

    try:
        # Attempt to make request
        result = oauth.access(
            client_id=PROJECT_INFO['client_id'],
            client_secret=PROJECT_INFO['client_secret'],
            redirect_uri=PROJECT_INFO['valid_url'],
            code=code
        )

    except Error as err:
        report_event('oauth_error', {
            'code': code,
            'error': str(err)
        })
        abort(400)

    if not result.successful:
        report_event('oauth_unsuccessful', {
            'code': code,
            'result': result.__dict__
        })
        abort(400)

    # Setup return info
    info = {
        'token': result.body['access_token'],
        'team_id': result.body['team_id'],
        'bot_id': result.body['bot']['bot_user_id'],
        'bot_token': result.body['bot']['bot_access_token']
    }

    # Return info
    return info
Example #13
0
def send_roll(team, roll, args):
    """Post the roll to Slack."""
    # Set up chat object
    chat = Chat(team.bot_token)

    try:
        # Attempt to post message
        chat.post_message(
            args['channel_id'],
            roll,
            username='******',
            icon_emoji=':game_die:'
        )

    except Error as err:
        report_event(str(err), {
            'team': team.__dict__,
            'roll': roll,
            'args': args
        })

        # Check specifically for channel errors
        if str(err) == 'channel_not_found':
            err_msg = "{0} is not authorized to post in this channel.".format(
                'The {0} bot'.format(PROJECT_INFO['name_full'])
            )
            err_msg += ' Please invite it to join this channel and try again.'
            return err_msg

        # Report any other errors
        return '{0} encountered an error: {1}'.format(
            PROJECT_INFO['name_full'],
            str(err)
        )

    # Return successful
    return
Example #14
0
def make_roll(args):
    """Run dice roll functions."""
    # Reset global error tracker
    global ERRORS
    ERRORS = []

    # Make sure this is a valid slash command
    if args['command'] not in ALLOWED_COMMANDS:
        report_event('command_not_allowed', args)
        return '"{0}" is not an allowed command'.format(args['command'])

    # Set global command value to access later
    global COMMAND
    COMMAND = args['command']

    # Check to see if team has authenticated with the app
    team = get_team(args)

    # If the user, team token, and bot token are not valid, let them know
    if (not team or not is_valid_token(team.token)
            or not is_valid_token(team.bot_token)):
        report_event('auth_error', {'args': args, 'team': team.__dict__})
        return AUTH_ERROR

    # If there's no input, use the default roll
    dice_roll = 'd6' if not args['text'] else args['text']

    # Get parser
    parser = get_parser()

    # Parse args
    result = parser.parse_args([dice_roll])

    # Report any errors from parser
    if ERRORS:
        report_event('parser_errors', {'errors': ERRORS})
        return ERRORS[0]

    # Get requested flip
    roll = do_roll(result, args['user_name'])

    # Post flip as user
    err = send_roll(team, roll, args)

    # If there were problems posting, report it
    if err is not None:
        return err

    # Return successful
    return ('', 204)
Example #15
0
    def __call__(self, parser, namespace, values, option_string=None):
        """Validate flip arguments and stores them to namespace."""
        dice_roll = values.lower()

        # Check for help
        if dice_roll in ['help', 'version']:
            parser.print_help(dice_roll)
            return

        # Set defaults
        count = 1
        sides = 6
        modifier = None
        modifier_count = None
        hit = None

        # Parse the roll
        result = re.match(
            r'(?P<count>\d+)?d(?P<sides>\d+)' +
            r'(?:(?P<mod>[-+])(?P<mod_count>\d+))?' +
            r'(?:\s(?P<hit>hit)(?P<hit_count>\d+)?)?', dice_roll, re.I)

        # Check that roll is valid
        if result is None:
            report_event('roll_invalid', {'roll': dice_roll})
            parser.error("'{0}' is not a valid roll format".format(dice_roll))

        else:
            # Get the number of dice
            if result.group('count') is not None:
                count = int(result.group('count'))

                # Set 100 count max
                if count > 100:
                    count = 100

                # Set 1 count min
                if count < 1:
                    count = 1

            # Get the number of sides
            if result.group('sides') is not None:
                sides = int(result.group('sides'))

                # Set 100 side max
                if sides > 100:
                    sides = 100

                # Set 2 side min
                if sides < 2:
                    sides = 2

            # Get the modifiers
            if result.group('mod') is not None:

                # Check that we have a number with the modifier
                if result.group('mod_count') is None:
                    report_event('roll_modifier_invalid', {'roll': dice_roll})
                    parser.error(
                        "'{0}' is not a valid roll format".format(dice_roll))

                # Set modifier data
                modifier = result.group('mod')
                modifier_count = int(result.group('mod_count'))

                # Set 100 modifier max
                if modifier_count > 100:
                    modifier_count = 100

            # Get the hit
            if result.group('hit') is not None:
                # Set default hit to 5 as per Shadowrun mechanics
                hit = 5

                # See if we have a hit threshold
                if result.group('hit_count') is not None:
                    hit = int(result.group('hit_count'))

                    # Set max hit to 100
                    if hit > 100:
                        hit = 100

                # Make sure we have enough sides
                if hit > sides:
                    report_event('roll_hit_invalid', {'roll': dice_roll})
                    parser.error("Hit threshold '{0}' is too big".format(hit))

        # Set values
        setattr(namespace, 'count', count)
        setattr(namespace, 'sides', sides)
        setattr(namespace, 'modifier', modifier)
        setattr(namespace, 'modifier_count', modifier_count)
        setattr(namespace, 'hit', hit)

        # Exit call
        return
Example #16
0
def make_roll(args):
    """Wrapper function for roll functions."""
    # Reset global error traker
    global ERRORS
    ERRORS = []
    print args

    # Make sure this is a valid slash command
    if args['command'] not in ALLOWED_COMMANDS:
        report_event('command_not_allowed', args)
        return '"{0}" is not an allowed command'.format(args['command'])

    else:
        # Set global command value to access later
        global COMMAND
        COMMAND = args['command']

    # Check to see if team has authenticated with the app
    team = get_team(args)

    # If the user, team token, and bot token are not valid, let them know
    if (
            not team or
            not is_valid_token(team.token) or
            not is_valid_token(team.bot_token)
    ):
        report_event('auth_error', {
            'args': args,
            'team': team.__dict__
        })
        return AUTH_ERROR

    # If there's no input, use the default roll
    if not args['text']:
        dice_roll = 'd6'
    else:
        dice_roll = args['text']

    # Get parser
    parser = get_parser()

    # Parse args
    result = parser.parse_args([dice_roll])

    # Report any errors from parser
    if len(ERRORS) > 0:
        report_event('parser_errors', {
            'errors': ERRORS
        })
        return ERRORS[0]

    # Get requested flip
    roll = do_roll(result, args['user_name'])

    # Post flip as user
    err = send_roll(team, roll, args)

    # If there were problems posting, report it
    if err is not None:
        return err

    # Return successful
    return ('', 204)
Example #17
0
    def __call__(self, parser, namespace, values, option_string=None):
        """Validate flip arguments and stores them to namespace."""
        dice_roll = values.lower()

        # Check for help
        if dice_roll in ['help', 'version']:
            parser.print_help(dice_roll)
            return

        # Set defaults
        count = 1
        sides = 6
        modifier = None
        modifier_count = None

        # Parse the roll
        result = re.match(r'(\d+)?d(\d+)(?:([-+])(\d+))?', dice_roll, re.I)

        # Check that roll is valid
        if result is None:
            report_event('roll_invalid', {
                'roll': dice_roll
            })
            parser.error(
                "'{0}' is not a recognized roll format".format(
                    dice_roll.encode('utf-8')
                )
            )

        else:
            # Get the number of dice
            if result.group(1) is not None:
                count = int(result.group(1))

                # Set 100 count max
                if count > 100:
                    count = 100

                # Set 1 count min
                if count < 1:
                    count = 1

            # Get the number of sides
            if result.group(2) is not None:
                sides = int(result.group(2))

                # Set 100 side max
                if sides > 100:
                    sides = 100

                # Set 4 side min
                if sides < 4:
                    sides = 4

            # Get the modifiers
            if result.group(3) is not None:

                if result.group(4) is None:
                    report_event('roll_modifier_invalid', {
                        'roll': dice_roll
                    })
                    parser.error(
                        "'{0}' is not a recognized roll format".format(
                            dice_roll.encode('utf-8')
                        )
                    )

                modifier = result.group(3)
                modifier_count = int(result.group(4))

        # Set values
        setattr(namespace, 'count', count)
        setattr(namespace, 'sides', sides)
        setattr(namespace, 'modifier', modifier)
        setattr(namespace, 'modifier_count', modifier_count)

        return