Exemplo n.º 1
0
def parse_active_membership(user_data: dict) -> Optional[Membership]:
    if not user_data or not user_data.get("data") or not user_data.get(
            "included"):
        return None

    if user_data["data"]["id"] in settings.PATREON_GOD_IDS:
        return Membership(
            platform=Platform.patreon,
            user_id=user_data["data"]["id"],
            full_name=user_data["data"]["attributes"]["full_name"],
            email=user_data["data"]["attributes"]["email"],
            image=user_data["data"]["attributes"]["image_url"],
            started_at=datetime.utcnow(),
            charged_at=None,
            expires_at=datetime.utcnow() + timedelta(days=100 * 365),
            lifetime_support_cents=-1,
            currently_entitled_amount_cents=0)

    log.info(f"Patreon active membership: {user_data}")

    for membership in user_data["included"]:
        if membership["attributes"]["patron_status"] == "active_patron" \
                and membership["attributes"]["last_charge_status"] == "Paid":

            now = datetime.utcnow()

            membership_started_at = datetime.strptime(
                str(membership["attributes"]
                    ["pledge_relationship_start"])[:10], "%Y-%m-%d"
            ) if membership["attributes"]["pledge_relationship_start"] else now

            last_charged_at = None
            if membership["attributes"]["last_charge_date"]:
                last_charged_at = datetime.strptime(
                    str(membership["attributes"]["last_charge_date"])[:10],
                    "%Y-%m-%d")

            if last_charged_at:
                membership_expires_at = last_charged_at + timedelta(days=45)
            else:
                membership_expires_at = first_day_of_next_month(
                    now) + timedelta(days=15)

            return Membership(
                platform=Platform.patreon,
                user_id=user_data["data"]["id"],
                full_name=user_data["data"]["attributes"]["full_name"],
                email=user_data["data"]["attributes"]["email"],
                image=None,  # user_data["data"]["attributes"]["image_url"],
                started_at=membership_started_at,
                charged_at=last_charged_at,
                expires_at=membership_expires_at,
                lifetime_support_cents=int(
                    membership["attributes"]["lifetime_support_cents"] or 0),
                currently_entitled_amount_cents=int(
                    membership["attributes"]["currently_entitled_amount_cents"]
                    or 0),
            )

    return None
Exemplo n.º 2
0
def patreon_oauth_callback(request):
    code = request.GET.get("code")
    if not code:
        return render(
            request, "error.html", {
                "message":
                "Что-то сломалось между нами и патреоном. Так бывает. Попробуйте залогиниться еще раз."
            })

    try:
        auth_data = patreon.fetch_auth_data(code)
        user_data = patreon.fetch_user_data(auth_data["access_token"])
    except PatreonException as ex:
        if "invalid_grant" in str(ex):
            return render(
                request, "error.html", {
                    "message":
                    "Тут такое дело. Авторизация патреона — говно. "
                    "Она не сразу понимает, что вы стали моим патроном и отдаёт мне ошибку. "
                    "Я уже написал им в саппорт, но пока вам надо немного подождать и авторизоваться снова. "
                    "Обычно тогда срабатывает. Если нет — напишите мне в личные сообщения на патреоне."
                })

        return render(
            request, "error.html", {
                "message":
                "Не получилось загрузить ваш профиль с серверов патреона. "
                "Попробуйте еще раз, наверняка оно починится. "
                f"Но если нет, то вот текст ошибки, с которым можно пожаловаться мне в личку:",
                "data":
                str(ex)
            })

    membership = patreon.parse_active_membership(user_data)
    if not membership:
        return render(
            request, "error.html", {
                "message":
                "Надо быть патроном чтобы состоять в клубе.<br>"
                '<a href="https://www.patreon.com/join/vas3k">Станьте им здесь!</a>'
            })

    now = datetime.utcnow()
    user, is_created = User.objects.get_or_create(
        membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON,
        membership_platform_id=membership.user_id,
        defaults=dict(
            email=membership.email,
            full_name=membership.full_name[:120],
            avatar=upload_image_from_url(membership.image)
            if membership.image else None,
            membership_started_at=membership.started_at,
            membership_expires_at=membership.expires_at,
            created_at=now,
            updated_at=now,
            is_email_verified=False,
            is_profile_complete=False,  # redirect new users to an intro page
        ),
    )

    if is_created:
        user.balance = membership.lifetime_support_cents / 100
    else:
        user.membership_expires_at = membership.expires_at
        user.balance = membership.lifetime_support_cents / 100  # TODO: remove when the real money comes in

    user.membership_platform_data = {
        "access_token": auth_data["access_token"],
        "refresh_token": auth_data["refresh_token"],
    }
    user.save()

    session = Session.objects.create(
        user=user,
        token=random_string(length=32),
        created_at=now,
        expires_at=first_day_of_next_month(now),
    )

    redirect_to = reverse("profile", args=[user.slug])

    state = request.GET.get("state")
    if state:
        redirect_to += f"?{state}"

    response = redirect(redirect_to)
    response.set_cookie(
        key="token",
        value=session.token,
        max_age=settings.SESSION_COOKIE_AGE,
        httponly=True,
        secure=not settings.DEBUG,
    )
    return response