Ejemplo n.º 1
0
def stripe_cancel():
    session.user.refresh()
    logger.info(f'Canceling Stripe subscription {session.user.stripe_subscription_id}')

    stripe.Subscription.modify(
        session.user.stripe_subscription_id,
        cancel_at_period_end=True
    )

    logger.info('Updating SubscriptionDetails record')
    try:
        sub = SubscriptionDetails.subscription_id_index.get(session.user.stripe_subscription_id)
        sub.canceled_timestamp = time.time()
        sub.canceled_internally = True
        sub.save()
    except:
        logger.exception('Failed to update SubscriptionDetails for canceled subscription')

    metrics.record('subscription.stripe.canceled_internally')
    metrics.event(
        'Stripe Subscription Canceled',
        f'User: {session.user_id} / {session.username}\n'
        f'Subscription ID: {session.user.stripe_subscription_id}\n',
        tags={
            'module': 'subscribe',
            'function': 'stripe_cancel',
        }
    )

    return redirect(url_for('subscribe.subscribe'))
Ejemplo n.º 2
0
def paypal_cancel():
    session.user.refresh()
    logger.info(f'Canceling PayPal subscription {session.user.paypal_subscr_id}')

    paypal_client.cancel_subscription(session.user.paypal_subscr_id)

    logger.info('Updating SubscriptionDetails record')
    try:
        sub = SubscriptionDetails.subscription_id_index.get(session.user.paypal_subscr_id)
        sub.canceled_timestamp = time.time()
        sub.canceled_internally = True
        sub.save()
    except:
        logger.exception('Failed to update SubscriptionDetails for canceled subscription')

    metrics.record('subscription.paypal.canceled_internally')
    metrics.event(
        'PayPal Subscription Canceled',
        f'User: {session.user_id} / {session.username}\n'
        f'Subscription ID: {session.user.paypal_subscr_id}\n',
        tags={
            'module': 'subscribe',
            'function': 'paypal_cancel',
        }
    )

    return redirect(url_for('subscribe.subscribe'))
Ejemplo n.º 3
0
def paypal_approved():
    logger.info(f'Paypal subscription approved: {request.form}')

    try:
        sub = paypal_client.get_subscription_details(
            request.form['subscriptionID'])
    except:
        logger.exception('Failed to get PayPal subscription details')
        return Response(status=400, response='Subscription not found')

    logger.info('Creating SubscriptionDetails record')
    SubscriptionDetails(user_id=session.user_id,
                        version=2,
                        type='paypal',
                        subscription_id=request.form['subscriptionID'],
                        full_data=request.form).save()

    logger.info('Updating User model')
    # TODO: use set actions
    session.user.refresh()
    session.user.subscription_active = True
    session.user.subscription_type = 'v2.paypal'
    session.user.paypal_subscr_id = request.form['subscriptionID']
    session.user.paypal_payer_email = None
    session.user.paypal_payer_id = None
    session.user.paypal_subscr_date = None
    session.user.paypal_cancel_at_period_end = None

    plan_name = 'UNKNOWN'
    try:
        session.user.paypal_payer_email = sub['subscriber']['email_address']
        session.user.paypal_subscr_date = sub['create_time']
        session.user.paypal_cancel_at_period_end = not sub['auto_renewal']

        logger.info(f'Checking PayPal plan details')
        try:
            plan = paypal_client.get_plan_details(sub['plan_id'])
        except:
            logger.exception('Failed to fetch PayPal plan details')
        else:
            plan_name = plan['description']

    except:
        logger.exception('Failed to get PayPal subscription details')

    session.user.save()

    metrics.record('subscription.paypal.approved')
    metrics.event('PayPal Subscription Created',
                  f'User: {session.user_id} / {session.username}\n'
                  f'Subscription ID: {session.user.paypal_subscr_id}\n'
                  f'Payer Email: {session.user.paypal_payer_email}\n'
                  f'Plan: {plan_name}',
                  tags={'type': 'user-subscription'})

    return Response(status=204)
Ejemplo n.º 4
0
    def delete_integration():
        logger.info(f'Decoding JWT payload')
        try:
            args = jwt.decode(request.form.get('args', ''),
                              HMAC_KEY,
                              algorithms=['HS256'])['args']
        except InvalidTokenError as e:
            logger.error(f'Failed to decode JWT token')
            return Response(json.dumps({'error': f'Invalid token'}),
                            status=403)
        logger.info(f'JWT payload: {args}')

        if args['action'] != 'delete':
            logger.error(
                f'Can\'t handle delete_integration request - unknown type {args["type"]}'
            )
            return Response(json.dumps({'error': 'Unknown request type'}),
                            status=400)

        if args['type'] == 'discord':
            notification = DiscordBotNotification.get(args['key'])
            logger.info(f'Deleting {notification}')
            notification.delete()

            try:
                logger.info(f'Updating announce message')
                get_message_r = discord_bot.get(
                    CHANNEL_GET_MESSAGE % (notification.channel_id,
                                           notification.announce_message_id))
                get_message_r.raise_for_status()
                print(get_message_r.json())

                content = get_message_r.json()['content']
                embed = get_message_r.json()['embeds'][0]
                embed.update({
                    'description': 'Integration deleted',
                    'color': 0xff0000
                })

                update_message_r = discord_bot.patch(
                    CHANNEL_EDIT_MESSAGE % (notification.channel_id,
                                            notification.announce_message_id),
                    json={
                        'content': f'~~{content}~~',
                        'embed': embed,
                    })
                update_message_r.raise_for_status()
            except Exception as e:
                logger.warning(f'Failed to update announce message: {e}')

        elif args['type'] == 'twitch':
            notification = TwitchBotNotification.get(args['key'])
            logger.info(f'Deleting {notification}')
            notification.delete()
        else:
            raise ValueError('Unknown type')

        metrics.event(f'{args["type"].title()} Bot Removed',
                      f'User: {session.username} ({session.user_id})\n' +
                      '\n'.join(f'{k}: {v}'
                                for k, v in notification.asdict().items()),
                      tags={
                          'module': 'notification_bot',
                          'function': 'delete_integration',
                          'game': game_name,
                      })

        return redirect(url_for(blueprint.name + '.root'))
Ejemplo n.º 5
0
    def add_to_channel():
        logger.info(f'Got add_to_channel with form {request.form}')

        if 'create_args' not in request.form and 'existing_args' not in request.form:
            return redirect(url_for(blueprint.name + '.root'))

        # Decode JWT `payload`
        # This ensures that the arguments (channel_id for existing channel, guild_id for creating channel) comes from the actual ouath'd user
        logger.info(f'Decoding JWT payload')
        try:
            args = jwt.decode(
                # try get create_args first, as this is on the "create" button
                # fall back to existing_args which is always present, but should only be used if "add" button was used
                request.form.get('create_args',
                                 request.form.get('existing_args')),
                HMAC_KEY,
                algorithms=['HS256'])['args']
        except InvalidTokenError as e:
            logger.error(f'Failed to decode JWT token')
            return Response(json.dumps({'error': f'Invalid token'}),
                            status=403)
        logger.info(f'JWT payload: {args}')

        if args['action'] == 'add_to_existing_channel':
            # using an existing channel - attempt to add SEND_MESSAGE permission for ourselves... don't mind if this errors
            channel_id = args['channel_id']
            logger.info(f'Using existing channel {channel_id} to post games')
            logger.info(f'Checking for post permission')
            added_post_permission = _add_post_permission(channel_id)
        elif args['action'] == 'create_channel':
            # creating a new channel - create this channel with SEND_MESSAGE for ourselves and view only for everyone else
            logger.info(
                f'Creating new channel {request.form["channel_name"]} to post games to'
            )
            channel_id = _create_channel(args['guild_id'],
                                         request.form['channel_name'],
                                         f'{game_title} Games | OverTrack.gg',
                                         restrict_posting=request.form.get(
                                             'restrict_post_messages',
                                             '') == 'on')
            if not channel_id:
                # if we didn't create the channel, complain
                return Response(
                    f'<html><head></head><body>'
                    f'<p>Did not have permission to create channel - '
                    f'please verify that the OverTrack bot has permission create a channel then <a href="{request.url}">retry</a></p>'
                    f'</body></html>',
                    content_type='text/html')
        else:
            logger.error(
                f'Can\'t handle add_to_channel request - unknown type {args["type"]}'
            )
            return Response(json.dumps({'error': 'Unknown integration type'}),
                            status=400)

        # Get the channel and guild name
        # This is used simply for recording in the database, so we can have channel names in the delete list
        channel_info, guild_info = _get_channel_and_guild_info(channel_id)

        parent_key = args.get('parent_key')
        is_parent = parent_key is None
        logger.info(f'Creating new discord bot (parent={parent_key})')

        # Try to send the greetings message
        logger.info(f'Sending intro message')

        content = f'OverTrack is now posting <@{args["discord_user_id"]}>\'s {game_title} games to this channel!\n\n'
        if parent_key is not None:
            description = f'Permission was granted because <@{args["discord_user_id"]}> has permissions to post messages in this channel.\n'
        else:
            description = f'Other users with permissions to post messages here will now be able to connect their account to this bot.\n'
        description += (
            f'To stop posting <@{args["discord_user_id"]}>\'s games here a channel admin can delete this message, '
            f'or <@{args["discord_user_id"]}> can remove their integration.\n')

        create_message_r = discord_bot.post(
            CHANNEL_CREATE_MESSAGE % (channel_id, ),
            json={
                'content': content,
                'embed': {
                    'description': description,
                    'url': 'https://overtrack.gg',
                    'color': 0xf79c15,
                    'author': {
                        'name':
                        'OverTrack.gg',
                        'url':
                        'https://overtrack.gg',
                        'icon_url':
                        f'https://cdn.overtrack.gg/static/images/{game_name}.png'
                    }
                },
            })
        if create_message_r.status_code == 403:
            logger.warning(
                f'Failed to send intro message: {create_message_r.status_code}'
            )
            return Response(
                f'<html><head></head><body>'
                f'<p>Could not post to channel - '
                f'please verify that the OverTrack bot has permission to post (or to modify channel permissions) then <a href="{request.url}">retry</a></p>'
                f'</body></html>',
                content_type='text/html')
        create_message_r.raise_for_status()
        create_message = create_message_r.json()
        print('Create message: ')
        print(create_message)
        logger.info(
            f'Intro message sent: {create_message_r.status_code} - id: {create_message["id"]}'
        )

        key = f'{session.user_id}.{game_name}.{channel_id}'
        if parent_key == key:
            logger.warning(f'Integration is overriding existing integration')
            is_parent = True
            # TODO: update parent message?

        # Save the notification settings
        notification = DiscordBotNotification.create(
            user_id=session.user_id,
            discord_user_id=args['discord_user_id'],
            announce_message_id=create_message['id'],
            game=game_name,
            channel_id=channel_id,
            guild_id=channel_info['guild_id'],
            guild_name=guild_info['name'],
            channel_name=channel_info['name'],
            notification_data={
                o.name: o.parse(request.form.getlist(o.name))
                for o in bot_options
            },
            is_parent=is_parent,
            autoapprove_children=is_parent,
            parent_key=parent_key,
        )
        logger.info(f'Created {notification}')
        notification_cache[notification.guild_id].append(notification)
        notification.save()

        metrics.event('Discord Bot Added',
                      f'User: {session.username} ({session.user_id})\n' +
                      '\n'.join(f'{k}: {v}'
                                for k, v in notification.asdict().items()),
                      tags={
                          'module': 'discord_bot',
                          'function': 'add_to_channel',
                          'game': game_name,
                      })

        return redirect(url_for(blueprint.name + '.root'))