WIP
This commit is contained in:
@ -1,6 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-3">{{ toc|safe }}</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="row">
|
||||
{% for game in games %}
|
||||
<div class="col-xl-3 col-lg-4 col-md-6 col-sm-12 mb-4">
|
||||
@ -26,4 +29,6 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
@ -3,7 +3,7 @@
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-3">{% include "partials/toc.html" %}</div>
|
||||
<div class="col-lg-3">{{ toc|safe }}</div>
|
||||
<div class="col-lg-9">
|
||||
{% include "partials/info_box.html" %}
|
||||
{% include "partials/news.html" %}
|
||||
|
@ -7,15 +7,15 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href='{% url "core:games" %}'>Games</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href='{% url "core:reward_campaigns" %}'>Reward campaigns</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href=''>API</a>
|
||||
</li>
|
||||
<li class="nav-item d-none d-sm-block">
|
||||
<a class="nav-link" href="https://github.com/sponsors/TheLovinator1">Donate</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href='{% url "core:reward_campaigns" %}'>Reward campaigns</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
@ -1,12 +0,0 @@
|
||||
<div class="position-sticky d-none d-lg-block toc">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div id="toc-list" class="list-group">
|
||||
{% for campaign in reward_campaigns %}
|
||||
<a class="list-group-item list-group-item-action plain-text-item"
|
||||
href="#reward-{{ campaign.id }}">{{ campaign }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,19 +0,0 @@
|
||||
<div class="position-sticky d-none d-lg-block toc">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div id="toc-list" class="list-group">
|
||||
<a class="list-group-item list-group-item-action plain-text-item"
|
||||
href="#info-box">Information</a>
|
||||
<a class="list-group-item list-group-item-action plain-text-item"
|
||||
href="#game-list">Site news</a>
|
||||
{% for org in orgs %}
|
||||
{{ org }}
|
||||
{% for game in org.games.all %}
|
||||
<a class="list-group-item list-group-item-action plain-text-item"
|
||||
href="#game-{{ game.game_id }}">{{ game.display_name }}</a>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -3,7 +3,7 @@
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-3">{% include "partials/reward_campaigns_toc.html" %}</div>
|
||||
<div class="col-lg-3">{{ toc }}</div>
|
||||
<div class="col-lg-9">
|
||||
<h2>Reward Campaigns</h2>
|
||||
<div>
|
||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from django.urls import URLPattern, URLResolver, path
|
||||
|
||||
from core.views import GameView, RewardCampaignView, index
|
||||
from core.views import game_view, index, reward_campaign_view
|
||||
|
||||
app_name: str = "core"
|
||||
|
||||
@ -10,12 +10,12 @@ urlpatterns: list[URLPattern | URLResolver] = [
|
||||
path(route="", view=index, name="index"),
|
||||
path(
|
||||
route="games/",
|
||||
view=GameView.as_view(),
|
||||
view=game_view,
|
||||
name="games",
|
||||
),
|
||||
path(
|
||||
route="reward_campaigns/",
|
||||
view=RewardCampaignView.as_view(),
|
||||
view=reward_campaign_view,
|
||||
name="reward_campaigns",
|
||||
),
|
||||
]
|
||||
|
@ -1,12 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import hishel
|
||||
from django.conf import settings
|
||||
from django.db.models.manager import BaseManager
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views.generic import ListView
|
||||
|
||||
from core.data import WebhookData
|
||||
from twitch_app.models import Game, RewardCampaign
|
||||
@ -58,26 +59,59 @@ def get_webhook_data(webhook: str) -> WebhookData:
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TOCItem:
|
||||
"""Table of contents item."""
|
||||
|
||||
name: str
|
||||
toc_id: str
|
||||
|
||||
|
||||
def build_toc(list_of_things: list[TOCItem]) -> str:
|
||||
"""Build the table of contents."""
|
||||
html: str = """
|
||||
<div class="position-sticky d-none d-lg-block toc">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div id="toc-list" class="list-group">
|
||||
"""
|
||||
|
||||
for item in list_of_things:
|
||||
html += (
|
||||
f'<a class="list-group-item list-group-item-action plain-text-item" href="#{item.toc_id}">{item.name}</a>'
|
||||
)
|
||||
html += """</div></div></div></div>"""
|
||||
return html
|
||||
|
||||
|
||||
def index(request: HttpRequest) -> HttpResponse:
|
||||
"""Render the index page."""
|
||||
reward_campaigns: BaseManager[RewardCampaign] = RewardCampaign.objects.all()
|
||||
|
||||
return TemplateResponse(
|
||||
request=request,
|
||||
template="index.html",
|
||||
context={"reward_campaigns": reward_campaigns},
|
||||
)
|
||||
toc: str = build_toc([
|
||||
TOCItem(name="Information", toc_id="#info-box"),
|
||||
TOCItem(name="Games", toc_id="#games"),
|
||||
])
|
||||
|
||||
context: dict[str, BaseManager[RewardCampaign] | str] = {"reward_campaigns": reward_campaigns, "toc": toc}
|
||||
return TemplateResponse(request=request, template="index.html", context=context)
|
||||
|
||||
|
||||
class GameView(ListView):
|
||||
model = Game
|
||||
template_name: str = "games.html"
|
||||
context_object_name: str = "games"
|
||||
paginate_by = 100
|
||||
def game_view(request: HttpRequest) -> HttpResponse:
|
||||
"""Render the game view page."""
|
||||
games: BaseManager[Game] = Game.objects.all()
|
||||
|
||||
tocs: list[TOCItem] = [
|
||||
TOCItem(name=game.display_name, toc_id=game.slug) for game in games if game.display_name and game.slug
|
||||
]
|
||||
toc: str = build_toc(tocs)
|
||||
|
||||
context: dict[str, BaseManager[Game] | str] = {"games": games, "toc": toc}
|
||||
return TemplateResponse(request=request, template="games.html", context=context)
|
||||
|
||||
|
||||
class RewardCampaignView(ListView):
|
||||
model = RewardCampaign
|
||||
template_name: str = "reward_campaigns.html"
|
||||
context_object_name: str = "reward_campaigns"
|
||||
paginate_by = 100
|
||||
def reward_campaign_view(request: HttpRequest) -> HttpResponse:
|
||||
"""Render the reward campaign view page."""
|
||||
reward_campaigns: BaseManager[RewardCampaign] = RewardCampaign.objects.all()
|
||||
context: dict[str, BaseManager[RewardCampaign]] = {"reward_campaigns": reward_campaigns}
|
||||
return TemplateResponse(request=request, template="reward_campaigns.html", context=context)
|
||||
|
380
testboi.py
Normal file
380
testboi.py
Normal file
@ -0,0 +1,380 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class ChannelTypename(Enum):
|
||||
CHANNEL = "Channel"
|
||||
GAME = "Game"
|
||||
ORGANIZATION = "Organization"
|
||||
|
||||
|
||||
class Channel:
|
||||
id: int
|
||||
display_name: str
|
||||
name: str
|
||||
typename: ChannelTypename
|
||||
|
||||
def __init__(self, id: int, display_name: str, name: str, typename: ChannelTypename) -> None:
|
||||
self.id = id
|
||||
self.display_name = display_name
|
||||
self.name = name
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class AllowTypename(Enum):
|
||||
DROP_CAMPAIGN_ACL = "DropCampaignACL"
|
||||
|
||||
|
||||
class Allow:
|
||||
channels: list[Channel] | None
|
||||
is_enabled: bool
|
||||
typename: AllowTypename
|
||||
|
||||
def __init__(self, channels: list[Channel] | None, is_enabled: bool, typename: AllowTypename) -> None:
|
||||
self.channels = channels
|
||||
self.is_enabled = is_enabled
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class SelfTypename(Enum):
|
||||
DROP_CAMPAIGN_SELF_EDGE = "DropCampaignSelfEdge"
|
||||
|
||||
|
||||
class Self:
|
||||
is_account_connected: bool
|
||||
typename: SelfTypename
|
||||
|
||||
def __init__(self, is_account_connected: bool, typename: SelfTypename) -> None:
|
||||
self.is_account_connected = is_account_connected
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class Game:
|
||||
id: int
|
||||
slug: str
|
||||
display_name: str
|
||||
typename: ChannelTypename
|
||||
|
||||
def __init__(self, id: int, slug: str, display_name: str, typename: ChannelTypename) -> None:
|
||||
self.id = id
|
||||
self.slug = slug
|
||||
self.display_name = display_name
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class PurpleOwner:
|
||||
id: UUID | int
|
||||
name: str | None
|
||||
typename: ChannelTypename
|
||||
display_name: str | None
|
||||
slug: str | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: UUID | int,
|
||||
name: str | None,
|
||||
typename: ChannelTypename,
|
||||
display_name: str | None,
|
||||
slug: str | None,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.typename = typename
|
||||
self.display_name = display_name
|
||||
self.slug = slug
|
||||
|
||||
|
||||
class Status(Enum):
|
||||
ACTIVE = "ACTIVE"
|
||||
EXPIRED = "EXPIRED"
|
||||
|
||||
|
||||
class GameClass:
|
||||
id: UUID | int
|
||||
name: str
|
||||
typename: ChannelTypename
|
||||
|
||||
def __init__(self, id: UUID | int, name: str, typename: ChannelTypename) -> None:
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class BenefitTypename(Enum):
|
||||
DROP_BENEFIT = "DropBenefit"
|
||||
|
||||
|
||||
class Benefit:
|
||||
id: str
|
||||
created_at: datetime
|
||||
entitlement_limit: int
|
||||
game: GameClass
|
||||
image_asset_url: str
|
||||
is_ios_available: bool
|
||||
name: str
|
||||
owner_organization: GameClass
|
||||
typename: BenefitTypename
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: str,
|
||||
created_at: datetime,
|
||||
entitlement_limit: int,
|
||||
game: GameClass,
|
||||
image_asset_url: str,
|
||||
is_ios_available: bool,
|
||||
name: str,
|
||||
owner_organization: GameClass,
|
||||
typename: BenefitTypename,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.created_at = created_at
|
||||
self.entitlement_limit = entitlement_limit
|
||||
self.game = game
|
||||
self.image_asset_url = image_asset_url
|
||||
self.is_ios_available = is_ios_available
|
||||
self.name = name
|
||||
self.owner_organization = owner_organization
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class BenefitEdgeTypename(Enum):
|
||||
DROP_BENEFIT_EDGE = "DropBenefitEdge"
|
||||
|
||||
|
||||
class BenefitEdge:
|
||||
benefit: Benefit
|
||||
entitlement_limit: int
|
||||
typename: BenefitEdgeTypename
|
||||
|
||||
def __init__(self, benefit: Benefit, entitlement_limit: int, typename: BenefitEdgeTypename) -> None:
|
||||
self.benefit = benefit
|
||||
self.entitlement_limit = entitlement_limit
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class TimeBasedDropTypename(Enum):
|
||||
TIME_BASED_DROP = "TimeBasedDrop"
|
||||
|
||||
|
||||
class TimeBasedDrop:
|
||||
id: UUID
|
||||
required_subs: int
|
||||
benefit_edges: list[BenefitEdge]
|
||||
end_at: datetime
|
||||
name: str
|
||||
precondition_drops: None
|
||||
required_minutes_watched: int
|
||||
start_at: datetime
|
||||
typename: TimeBasedDropTypename
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: UUID,
|
||||
required_subs: int,
|
||||
benefit_edges: list[BenefitEdge],
|
||||
end_at: datetime,
|
||||
name: str,
|
||||
precondition_drops: None,
|
||||
required_minutes_watched: int,
|
||||
start_at: datetime,
|
||||
typename: TimeBasedDropTypename,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.required_subs = required_subs
|
||||
self.benefit_edges = benefit_edges
|
||||
self.end_at = end_at
|
||||
self.name = name
|
||||
self.precondition_drops = precondition_drops
|
||||
self.required_minutes_watched = required_minutes_watched
|
||||
self.start_at = start_at
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class DropCampaignTypename(Enum):
|
||||
DROP_CAMPAIGN = "DropCampaign"
|
||||
|
||||
|
||||
class PurpleDropCampaign:
|
||||
id: UUID
|
||||
drop_campaign_self: Self
|
||||
allow: Allow
|
||||
account_link_url: str
|
||||
description: str
|
||||
details_url: str
|
||||
end_at: datetime
|
||||
event_based_drops: list[Any]
|
||||
game: Game
|
||||
image_url: str
|
||||
name: str
|
||||
owner: PurpleOwner
|
||||
start_at: datetime
|
||||
status: Status
|
||||
time_based_drops: list[TimeBasedDrop]
|
||||
typename: DropCampaignTypename
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: UUID,
|
||||
drop_campaign_self: Self,
|
||||
allow: Allow,
|
||||
account_link_url: str,
|
||||
description: str,
|
||||
details_url: str,
|
||||
end_at: datetime,
|
||||
event_based_drops: list[Any],
|
||||
game: Game,
|
||||
image_url: str,
|
||||
name: str,
|
||||
owner: PurpleOwner,
|
||||
start_at: datetime,
|
||||
status: Status,
|
||||
time_based_drops: list[TimeBasedDrop],
|
||||
typename: DropCampaignTypename,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.drop_campaign_self = drop_campaign_self
|
||||
self.allow = allow
|
||||
self.account_link_url = account_link_url
|
||||
self.description = description
|
||||
self.details_url = details_url
|
||||
self.end_at = end_at
|
||||
self.event_based_drops = event_based_drops
|
||||
self.game = game
|
||||
self.image_url = image_url
|
||||
self.name = name
|
||||
self.owner = owner
|
||||
self.start_at = start_at
|
||||
self.status = status
|
||||
self.time_based_drops = time_based_drops
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class UserTypename(Enum):
|
||||
USER = "User"
|
||||
|
||||
|
||||
class PurpleUser:
|
||||
id: int
|
||||
drop_campaign: PurpleDropCampaign
|
||||
typename: UserTypename
|
||||
|
||||
def __init__(self, id: int, drop_campaign: PurpleDropCampaign, typename: UserTypename) -> None:
|
||||
self.id = id
|
||||
self.drop_campaign = drop_campaign
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class DropCampaign100_Data:
|
||||
user: PurpleUser
|
||||
|
||||
def __init__(self, user: PurpleUser) -> None:
|
||||
self.user = user
|
||||
|
||||
|
||||
class OperationName(Enum):
|
||||
DROP_CAMPAIGN_DETAILS = "DropCampaignDetails"
|
||||
|
||||
|
||||
class Extensions:
|
||||
duration_milliseconds: int
|
||||
operation_name: OperationName
|
||||
request_id: str
|
||||
|
||||
def __init__(self, duration_milliseconds: int, operation_name: OperationName, request_id: str) -> None:
|
||||
self.duration_milliseconds = duration_milliseconds
|
||||
self.operation_name = operation_name
|
||||
self.request_id = request_id
|
||||
|
||||
|
||||
class DropCampaign99:
|
||||
data: DropCampaign100_Data
|
||||
extensions: Extensions
|
||||
|
||||
def __init__(self, data: DropCampaign100_Data, extensions: Extensions) -> None:
|
||||
self.data = data
|
||||
self.extensions = extensions
|
||||
|
||||
|
||||
class FluffyDropCampaign:
|
||||
id: UUID
|
||||
drop_campaign_self: Self
|
||||
allow: Allow
|
||||
account_link_url: str
|
||||
description: str
|
||||
details_url: str
|
||||
end_at: datetime
|
||||
event_based_drops: list[Any]
|
||||
game: Game
|
||||
image_url: str
|
||||
name: str
|
||||
owner: GameClass
|
||||
start_at: datetime
|
||||
status: Status
|
||||
time_based_drops: list[TimeBasedDrop]
|
||||
typename: DropCampaignTypename
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: UUID,
|
||||
drop_campaign_self: Self,
|
||||
allow: Allow,
|
||||
account_link_url: str,
|
||||
description: str,
|
||||
details_url: str,
|
||||
end_at: datetime,
|
||||
event_based_drops: list[Any],
|
||||
game: Game,
|
||||
image_url: str,
|
||||
name: str,
|
||||
owner: GameClass,
|
||||
start_at: datetime,
|
||||
status: Status,
|
||||
time_based_drops: list[TimeBasedDrop],
|
||||
typename: DropCampaignTypename,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.drop_campaign_self = drop_campaign_self
|
||||
self.allow = allow
|
||||
self.account_link_url = account_link_url
|
||||
self.description = description
|
||||
self.details_url = details_url
|
||||
self.end_at = end_at
|
||||
self.event_based_drops = event_based_drops
|
||||
self.game = game
|
||||
self.image_url = image_url
|
||||
self.name = name
|
||||
self.owner = owner
|
||||
self.start_at = start_at
|
||||
self.status = status
|
||||
self.time_based_drops = time_based_drops
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class FluffyUser:
|
||||
id: int
|
||||
drop_campaign: FluffyDropCampaign
|
||||
typename: UserTypename
|
||||
|
||||
def __init__(self, id: int, drop_campaign: FluffyDropCampaign, typename: UserTypename) -> None:
|
||||
self.id = id
|
||||
self.drop_campaign = drop_campaign
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class DropCampaign109_Data:
|
||||
user: FluffyUser
|
||||
|
||||
def __init__(self, user: FluffyUser) -> None:
|
||||
self.user = user
|
||||
|
||||
|
||||
class DropCampaign149:
|
||||
data: DropCampaign109_Data
|
||||
extensions: Extensions
|
||||
|
||||
def __init__(self, data: DropCampaign109_Data, extensions: Extensions) -> None:
|
||||
self.data = data
|
||||
self.extensions = extensions
|
252
testboi2.py
Normal file
252
testboi2.py
Normal file
@ -0,0 +1,252 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class SelfTypename(Enum):
|
||||
DROP_CAMPAIGN_SELF_EDGE = "DropCampaignSelfEdge"
|
||||
|
||||
|
||||
class Self:
|
||||
is_account_connected: bool
|
||||
typename: SelfTypename
|
||||
|
||||
def __init__(self, is_account_connected: bool, typename: SelfTypename) -> None:
|
||||
self.is_account_connected = is_account_connected
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class GameTypename(Enum):
|
||||
GAME = "Game"
|
||||
|
||||
|
||||
class Game:
|
||||
id: int
|
||||
display_name: str
|
||||
box_art_url: str
|
||||
typename: GameTypename
|
||||
|
||||
def __init__(self, id: int, display_name: str, box_art_url: str, typename: GameTypename) -> None:
|
||||
self.id = id
|
||||
self.display_name = display_name
|
||||
self.box_art_url = box_art_url
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class OwnerTypename(Enum):
|
||||
ORGANIZATION = "Organization"
|
||||
|
||||
|
||||
class Owner:
|
||||
id: UUID
|
||||
name: str
|
||||
typename: OwnerTypename
|
||||
|
||||
def __init__(self, id: UUID, name: str, typename: OwnerTypename) -> None:
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class Status(Enum):
|
||||
ACTIVE = "ACTIVE"
|
||||
EXPIRED = "EXPIRED"
|
||||
|
||||
|
||||
class DropCampaignTypename(Enum):
|
||||
DROP_CAMPAIGN = "DropCampaign"
|
||||
|
||||
|
||||
class DropCampaign:
|
||||
id: UUID
|
||||
name: str
|
||||
owner: Owner
|
||||
game: Game
|
||||
status: Status
|
||||
start_at: datetime
|
||||
end_at: datetime
|
||||
details_url: str
|
||||
account_link_url: str
|
||||
drop_campaign_self: Self
|
||||
typename: DropCampaignTypename
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: UUID,
|
||||
name: str,
|
||||
owner: Owner,
|
||||
game: Game,
|
||||
status: Status,
|
||||
start_at: datetime,
|
||||
end_at: datetime,
|
||||
details_url: str,
|
||||
account_link_url: str,
|
||||
drop_campaign_self: Self,
|
||||
typename: DropCampaignTypename,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.owner = owner
|
||||
self.game = game
|
||||
self.status = status
|
||||
self.start_at = start_at
|
||||
self.end_at = end_at
|
||||
self.details_url = details_url
|
||||
self.account_link_url = account_link_url
|
||||
self.drop_campaign_self = drop_campaign_self
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class CurrentUser:
|
||||
id: int
|
||||
login: str
|
||||
drop_campaigns: list[DropCampaign]
|
||||
typename: str
|
||||
|
||||
def __init__(self, id: int, login: str, drop_campaigns: list[DropCampaign], typename: str) -> None:
|
||||
self.id = id
|
||||
self.login = login
|
||||
self.drop_campaigns = drop_campaigns
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class Image:
|
||||
image1_x_url: str
|
||||
typename: str
|
||||
|
||||
def __init__(self, image1_x_url: str, typename: str) -> None:
|
||||
self.image1_x_url = image1_x_url
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class Reward:
|
||||
id: UUID
|
||||
name: str
|
||||
banner_image: Image
|
||||
thumbnail_image: Image
|
||||
earnable_until: datetime
|
||||
redemption_instructions: str
|
||||
redemption_url: str
|
||||
typename: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: UUID,
|
||||
name: str,
|
||||
banner_image: Image,
|
||||
thumbnail_image: Image,
|
||||
earnable_until: datetime,
|
||||
redemption_instructions: str,
|
||||
redemption_url: str,
|
||||
typename: str,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.banner_image = banner_image
|
||||
self.thumbnail_image = thumbnail_image
|
||||
self.earnable_until = earnable_until
|
||||
self.redemption_instructions = redemption_instructions
|
||||
self.redemption_url = redemption_url
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class UnlockRequirements:
|
||||
subs_goal: int
|
||||
minute_watched_goal: int
|
||||
typename: str
|
||||
|
||||
def __init__(self, subs_goal: int, minute_watched_goal: int, typename: str) -> None:
|
||||
self.subs_goal = subs_goal
|
||||
self.minute_watched_goal = minute_watched_goal
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class RewardCampaignsAvailableToUser:
|
||||
id: UUID
|
||||
name: str
|
||||
brand: str
|
||||
starts_at: datetime
|
||||
ends_at: datetime
|
||||
status: str
|
||||
summary: str
|
||||
instructions: str
|
||||
external_url: str
|
||||
reward_value_url_param: str
|
||||
about_url: str
|
||||
is_sitewide: bool
|
||||
game: None
|
||||
unlock_requirements: UnlockRequirements
|
||||
image: Image
|
||||
rewards: list[Reward]
|
||||
typename: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: UUID,
|
||||
name: str,
|
||||
brand: str,
|
||||
starts_at: datetime,
|
||||
ends_at: datetime,
|
||||
status: str,
|
||||
summary: str,
|
||||
instructions: str,
|
||||
external_url: str,
|
||||
reward_value_url_param: str,
|
||||
about_url: str,
|
||||
is_sitewide: bool,
|
||||
game: None,
|
||||
unlock_requirements: UnlockRequirements,
|
||||
image: Image,
|
||||
rewards: list[Reward],
|
||||
typename: str,
|
||||
) -> None:
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.brand = brand
|
||||
self.starts_at = starts_at
|
||||
self.ends_at = ends_at
|
||||
self.status = status
|
||||
self.summary = summary
|
||||
self.instructions = instructions
|
||||
self.external_url = external_url
|
||||
self.reward_value_url_param = reward_value_url_param
|
||||
self.about_url = about_url
|
||||
self.is_sitewide = is_sitewide
|
||||
self.game = game
|
||||
self.unlock_requirements = unlock_requirements
|
||||
self.image = image
|
||||
self.rewards = rewards
|
||||
self.typename = typename
|
||||
|
||||
|
||||
class Data:
|
||||
current_user: CurrentUser
|
||||
reward_campaigns_available_to_user: list[RewardCampaignsAvailableToUser]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
current_user: CurrentUser,
|
||||
reward_campaigns_available_to_user: list[RewardCampaignsAvailableToUser],
|
||||
) -> None:
|
||||
self.current_user = current_user
|
||||
self.reward_campaigns_available_to_user = reward_campaigns_available_to_user
|
||||
|
||||
|
||||
class Extensions:
|
||||
duration_milliseconds: int
|
||||
operation_name: str
|
||||
request_id: str
|
||||
|
||||
def __init__(self, duration_milliseconds: int, operation_name: str, request_id: str) -> None:
|
||||
self.duration_milliseconds = duration_milliseconds
|
||||
self.operation_name = operation_name
|
||||
self.request_id = request_id
|
||||
|
||||
|
||||
class RewardCampaign11:
|
||||
data: Data
|
||||
extensions: Extensions
|
||||
|
||||
def __init__(self, data: Data, extensions: Extensions) -> None:
|
||||
self.data = data
|
||||
self.extensions = extensions
|
@ -28,6 +28,7 @@ from twitch_app.models import (
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from playwright.async_api._generated import BrowserContext, Page
|
||||
import json
|
||||
|
||||
# Where to store the Chrome profile
|
||||
data_dir = Path(
|
||||
@ -66,6 +67,7 @@ async def add_or_get_game(json_data: dict, name: str) -> tuple[Game | None, bool
|
||||
"slug": json_data.get("slug"),
|
||||
"display_name": json_data.get("displayName"),
|
||||
"typename": json_data.get("__typename"),
|
||||
"box_art_url": json_data.get("boxArtURL"), # Only for RewardCampaigns
|
||||
},
|
||||
)
|
||||
|
||||
@ -204,6 +206,9 @@ async def add_or_get_drop_campaign(
|
||||
logger.warning("No drop campaign data found")
|
||||
return None, False
|
||||
|
||||
if drop_campaign_data.get("__typename") != "Game":
|
||||
logger.error("__typename is not 'Game' for %s", drop_campaign_data.get("name", "Unknown Drop Campaign"))
|
||||
|
||||
drop_campaign, _ = await DropCampaign.objects.aupdate_or_create(
|
||||
id=drop_campaign_data["id"],
|
||||
defaults={
|
||||
@ -546,16 +551,35 @@ class Command(BaseCommand):
|
||||
continue
|
||||
|
||||
if "rewardCampaignsAvailableToUser" in campaign["data"]:
|
||||
# Save to folder named "reward_campaigns"
|
||||
dir_name: Path = Path("reward_campaigns")
|
||||
dir_name.mkdir(parents=True, exist_ok=True)
|
||||
with open(file=Path(dir_name / f"reward_campaign_{num}.json"), mode="w", encoding="utf-8") as f:
|
||||
json.dump(campaign, f, indent=4)
|
||||
|
||||
await add_reward_campaign(campaign)
|
||||
|
||||
if "dropCampaign" in campaign.get("data", {}).get("user", {}):
|
||||
if not campaign["data"]["user"]["dropCampaign"]:
|
||||
logger.warning("No drop campaign found")
|
||||
continue
|
||||
|
||||
# Save to folder named "drop_campaign"
|
||||
dir_name: Path = Path("drop_campaign")
|
||||
dir_name.mkdir(parents=True, exist_ok=True)
|
||||
with open(file=Path(dir_name / f"drop_campaign_{num}.json"), mode="w", encoding="utf-8") as f:
|
||||
json.dump(campaign, f, indent=4)
|
||||
|
||||
await add_drop_campaign(campaign)
|
||||
|
||||
if "dropCampaigns" in campaign.get("data", {}).get("user", {}):
|
||||
for drop_campaign in campaign["data"]["user"]["dropCampaigns"]:
|
||||
# Save to folder named "drop_campaigns"
|
||||
dir_name: Path = Path("drop_campaigns")
|
||||
dir_name.mkdir(parents=True, exist_ok=True)
|
||||
with open(file=Path(dir_name / f"drop_campaign_{num}.json"), mode="w", encoding="utf-8") as f:
|
||||
json.dump(drop_campaign, f, indent=4)
|
||||
|
||||
await add_drop_campaign(drop_campaign)
|
||||
|
||||
return json_data
|
||||
|
@ -0,0 +1,135 @@
|
||||
# Generated by Django 5.1rc1 on 2024-08-02 01:20
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.db.models.manager
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("twitch_app", "0009_alter_benefit_entitlement_limit_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="FrontEndChannel",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("name", models.TextField(blank=True, null=True)),
|
||||
("twitch_url", models.URLField(blank=True, null=True)),
|
||||
("live", models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
"base_manager_name": "prefetch_manager",
|
||||
},
|
||||
managers=[
|
||||
("objects", django.db.models.manager.Manager()),
|
||||
("prefetch_manager", django.db.models.manager.Manager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="FrontEndGame",
|
||||
fields=[
|
||||
("twitch_id", models.TextField(primary_key=True, serialize=False)),
|
||||
("game_url", models.URLField(blank=True, null=True)),
|
||||
("display_name", models.TextField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
"base_manager_name": "prefetch_manager",
|
||||
},
|
||||
managers=[
|
||||
("objects", django.db.models.manager.Manager()),
|
||||
("prefetch_manager", django.db.models.manager.Manager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="FrontEndOrg",
|
||||
fields=[
|
||||
("id", models.TextField(primary_key=True, serialize=False)),
|
||||
("name", models.TextField(blank=True, null=True)),
|
||||
("url", models.TextField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
"base_manager_name": "prefetch_manager",
|
||||
},
|
||||
managers=[
|
||||
("objects", django.db.models.manager.Manager()),
|
||||
("prefetch_manager", django.db.models.manager.Manager()),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="game",
|
||||
name="box_art_url",
|
||||
field=models.URLField(blank=True, null=True),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="FrontEndDropCampaign",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("account_link_url", models.URLField(blank=True, null=True)),
|
||||
("about_url", models.URLField(blank=True, null=True)),
|
||||
("ends_at", models.DateTimeField(null=True)),
|
||||
("starts_at", models.DateTimeField(null=True)),
|
||||
("channels", models.ManyToManyField(related_name="drop_campaigns", to="twitch_app.frontendchannel")),
|
||||
(
|
||||
"game",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="drop_campaigns",
|
||||
to="twitch_app.frontendgame",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
"base_manager_name": "prefetch_manager",
|
||||
},
|
||||
managers=[
|
||||
("objects", django.db.models.manager.Manager()),
|
||||
("prefetch_manager", django.db.models.manager.Manager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="FrontEndDrop",
|
||||
fields=[
|
||||
("id", models.TextField(primary_key=True, serialize=False)),
|
||||
("created_at", models.DateTimeField(null=True)),
|
||||
("name", models.TextField(blank=True, null=True)),
|
||||
("image_url", models.URLField(blank=True, null=True)),
|
||||
("limit", models.PositiveBigIntegerField(null=True)),
|
||||
("is_ios_available", models.BooleanField(null=True)),
|
||||
("minutes_watched", models.PositiveBigIntegerField(null=True)),
|
||||
(
|
||||
"drop_campaign",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="drops",
|
||||
to="twitch_app.frontenddropcampaign",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
"base_manager_name": "prefetch_manager",
|
||||
},
|
||||
managers=[
|
||||
("objects", django.db.models.manager.Manager()),
|
||||
("prefetch_manager", django.db.models.manager.Manager()),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="frontendgame",
|
||||
name="org",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="games",
|
||||
to="twitch_app.frontendorg",
|
||||
),
|
||||
),
|
||||
]
|
@ -1,3 +1,5 @@
|
||||
import typing
|
||||
|
||||
import auto_prefetch
|
||||
from django.db import models
|
||||
|
||||
@ -25,6 +27,7 @@ class Game(auto_prefetch.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
slug = models.TextField(null=True, blank=True)
|
||||
display_name = models.TextField(null=True, blank=True)
|
||||
box_art_url = models.URLField(null=True, blank=True)
|
||||
typename = models.TextField(null=True, blank=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
@ -502,6 +505,11 @@ class DropCampaign(auto_prefetch.Model):
|
||||
typename (str): The type name of the object, typically "DropCampaign".
|
||||
"""
|
||||
|
||||
STATUS_CHOICES: typing.ClassVar[list[tuple[str, str]]] = [
|
||||
("ACTIVE", "Active"),
|
||||
("EXPIRED", "Expired"),
|
||||
]
|
||||
|
||||
id = models.TextField(primary_key=True)
|
||||
allow = auto_prefetch.ForeignKey(Allow, on_delete=models.CASCADE, related_name="drop_campaigns", null=True)
|
||||
account_link_url = models.URLField(null=True, blank=True)
|
||||
@ -514,9 +522,68 @@ class DropCampaign(auto_prefetch.Model):
|
||||
name = models.TextField(null=True, blank=True)
|
||||
owner = auto_prefetch.ForeignKey(Owner, on_delete=models.CASCADE, related_name="drop_campaigns", null=True)
|
||||
starts_at = models.DateTimeField(null=True)
|
||||
status = models.TextField(null=True, blank=True)
|
||||
status = models.TextField(choices=STATUS_CHOICES, null=True, blank=True)
|
||||
time_based_drops = models.ManyToManyField(TimeBasedDrop, related_name="drop_campaigns")
|
||||
typename = models.TextField(null=True, blank=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name or "Unknown"
|
||||
|
||||
|
||||
class FrontEndChannel(auto_prefetch.Model):
|
||||
"""This is the channel we will see on the front end."""
|
||||
|
||||
name = models.TextField(null=True, blank=True)
|
||||
twitch_url = models.URLField(null=True, blank=True)
|
||||
live = models.BooleanField(default=False)
|
||||
|
||||
|
||||
class FrontEndOrg(auto_prefetch.Model):
|
||||
"""Drops are group by organization -> by game -> by drop campaign."""
|
||||
|
||||
id = models.TextField(primary_key=True)
|
||||
name = models.TextField(null=True, blank=True)
|
||||
url = models.TextField(null=True, blank=True)
|
||||
|
||||
|
||||
class FrontEndGame(auto_prefetch.Model):
|
||||
"""This is the game we will see on the front end."""
|
||||
|
||||
twitch_id = models.TextField(primary_key=True)
|
||||
game_url = models.URLField(null=True, blank=True)
|
||||
display_name = models.TextField(null=True, blank=True)
|
||||
|
||||
org = models.ForeignKey(FrontEndOrg, on_delete=models.CASCADE, related_name="games", null=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.display_name or "Unknown"
|
||||
|
||||
|
||||
class FrontEndDropCampaign(auto_prefetch.Model):
|
||||
"""This is the drop campaign we will see on the front end."""
|
||||
|
||||
account_link_url = models.URLField(null=True, blank=True)
|
||||
about_url = models.URLField(null=True, blank=True)
|
||||
|
||||
ends_at = models.DateTimeField(null=True)
|
||||
starts_at = models.DateTimeField(null=True)
|
||||
|
||||
game = models.ForeignKey(FrontEndGame, on_delete=models.CASCADE, related_name="drop_campaigns", null=True)
|
||||
|
||||
channels = models.ManyToManyField(FrontEndChannel, related_name="drop_campaigns")
|
||||
|
||||
|
||||
class FrontEndDrop(auto_prefetch.Model):
|
||||
"""This is the drop we will see on the front end."""
|
||||
|
||||
id = models.TextField(primary_key=True)
|
||||
created_at = models.DateTimeField(null=True)
|
||||
|
||||
name = models.TextField(null=True, blank=True)
|
||||
image_url = models.URLField(null=True, blank=True)
|
||||
|
||||
drop_campaign = models.ForeignKey(FrontEndDropCampaign, on_delete=models.CASCADE, related_name="drops", null=True)
|
||||
|
||||
limit = models.PositiveBigIntegerField(null=True)
|
||||
is_ios_available = models.BooleanField(null=True)
|
||||
minutes_watched = models.PositiveBigIntegerField(null=True)
|
||||
|
Reference in New Issue
Block a user