Add Reward Campaigns

This commit is contained in:
Joakim Hellsén 2026-01-14 22:29:15 +01:00
commit 1a71809460
No known key found for this signature in database
14 changed files with 1188 additions and 20 deletions

View file

@ -155,17 +155,20 @@
</style>
</head>
<body>
<strong>Twitch:</strong>
<a href="{% url 'twitch:dashboard' %}">Dashboard</a> |
<a href="{% url 'twitch:campaign_list' %}">Campaigns</a> |
<a href="{% url 'twitch:reward_campaign_list' %}">Rewards</a> |
<a href="{% url 'twitch:game_list' %}">Games</a> |
<a href="{% url 'twitch:org_list' %}">Organizations</a> |
<a href="{% url 'twitch:org_list' %}">Orgs</a> |
<a href="{% url 'twitch:channel_list' %}">Channels</a> |
<a href="{% url 'twitch:docs_rss' %}">RSS</a> |
<a href="{% url 'twitch:debug' %}">Debug</a> |
<a href="{% url 'twitch:emote_gallery' %}">Emotes</a>
<form action="{% url 'twitch:search' %}"
<br />
<a href="{% url 'twitch:docs_rss' %}">RSS</a> | <a href="{% url 'twitch:debug' %}">Debug</a>
<form action="{% url 'twitch:search' }}"
method="get"
style="display: inline">
style="display: inline;
margin-left: 1rem">
<input type="search"
name="q"
placeholder="Search..."

View file

@ -27,7 +27,8 @@ Hover over the end time to see the exact date and time.
<div style="font-size: 0.9rem; color: #666;">
Organizations:
{% for org in game_data.owners %}
<a href="{% url 'twitch:organization_detail' org.twitch_id %}">{{ org.name }}</a>{% if not forloop.last %}, {% endif %}
<a href="{% url 'twitch:organization_detail' org.twitch_id %}">{{ org.name }}</a>
{% if not forloop.last %},{% endif %}
{% endfor %}
</div>
{% endif %}
@ -151,5 +152,96 @@ Hover over the end time to see the exact date and time.
{% else %}
<p>No active campaigns at the moment.</p>
{% endif %}
<!-- Reward Campaigns Section -->
{% if active_reward_campaigns %}
<section id="reward-campaigns-section"
style="margin-top: 2rem;
border-top: 2px solid #ddd;
padding-top: 1rem">
<header style="margin-bottom: 1rem;">
<h2 style="margin: 0 0 0.5rem 0;">
<a href="{% url 'twitch:reward_campaign_list' %}">Reward Campaigns (Quest Rewards)</a>
</h2>
<p style="font-size: 0.9rem; color: #666; margin: 0.5rem 0 0 0;">Complete quests to earn rewards</p>
</header>
<div style="display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))">
{% for campaign in active_reward_campaigns %}
<article id="reward-campaign-{{ campaign.twitch_id }}"
style="border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem">
<h3 style="margin: 0 0 0.5rem 0;">
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">
{% if campaign.brand %}
{{ campaign.brand }}: {{ campaign.name }}
{% else %}
{{ campaign.name }}
{% endif %}
</a>
</h3>
{% if campaign.summary %}
<p style="font-size: 0.9rem; color: #555; margin: 0.5rem 0;">{{ campaign.summary }}</p>
{% endif %}
<div style="font-size: 0.85rem; color: #666;">
<p style="margin: 0.25rem 0;">
<strong>Status:</strong>
{% if campaign.is_active %}
<span style="color: green;">Active</span>
{% elif campaign.starts_at > now %}
<span style="color: orange;">Upcoming</span>
{% else %}
<span style="color: red;">Expired</span>
{% endif %}
</p>
{% if campaign.starts_at %}
<p style="margin: 0.25rem 0;">
<strong>Starts:</strong>
<time datetime="{{ campaign.starts_at|date:'c' }}"
title="{{ campaign.starts_at|date:'DATETIME_FORMAT' }}">
{{ campaign.starts_at|date:"M d, Y H:i" }}
</time>
</p>
{% endif %}
{% if campaign.ends_at %}
<p style="margin: 0.25rem 0;">
<strong>Ends:</strong>
<time datetime="{{ campaign.ends_at|date:'c' }}"
title="{{ campaign.ends_at|date:'DATETIME_FORMAT' }}">
{{ campaign.ends_at|date:"M d, Y H:i" }}
</time>
</p>
{% endif %}
{% if campaign.game %}
<p style="margin: 0.25rem 0;">
<strong>Game:</strong>
<a href="{% url 'twitch:game_detail' campaign.game.twitch_id %}">{{ campaign.game.display_name }}</a>
</p>
{% elif campaign.is_sitewide %}
<p style="margin: 0.25rem 0;">
<strong>Type:</strong> Site-wide reward campaign
</p>
{% endif %}
</div>
{% if campaign.external_url %}
<div style="margin-top: 0.75rem;">
<a href="{{ campaign.external_url }}"
target="_blank"
rel="noopener noreferrer"
style="display: inline-block;
padding: 0.5rem 1rem;
background-color: #9146ff;
color: white;
border-radius: 4px;
text-decoration: none;
font-size: 0.9rem">Claim Reward</a>
</div>
{% endif %}
</article>
{% endfor %}
</div>
</section>
{% endif %}
</main>
{% endblock content %}

View file

@ -1,14 +1,31 @@
{% extends "base.html" %}
{% block title %}Emotes{% endblock %}
{% block title %}
Emotes
{% endblock title %}
{% block content %}
<h1>Emotes</h1>
<div class="emote-gallery" style="display: flex; flex-wrap: wrap; gap: 1.5rem; justify-content: flex-start;">
{% for emote in emotes %}
<a href="{% url 'twitch:campaign_detail' emote.campaign.twitch_id %}" title="{{ emote.campaign.name }}" style="display: inline-block;">
<img src="{{ emote.image_url }}" alt="Emote" style="max-width: 96px; max-height: 96px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.12); padding: 4px;" loading="lazy" />
</a>
{% empty %}
<p>No drop campaigns with emotes found.</p>
{% endfor %}
</div>
{% endblock %}
<h1>Emotes</h1>
<div class="emote-gallery"
style="display: flex;
flex-wrap: wrap;
gap: 1.5rem;
justify-content: flex-start">
{% for emote in emotes %}
<a href="{% url 'twitch:campaign_detail' emote.campaign.twitch_id %}"
title="{{ emote.campaign.name }}"
style="display: inline-block">
<img src="{{ emote.image_url }}"
height="96"
width="96"
alt="Emote"
style="max-width: 96px;
max-height: 96px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
padding: 4px"
loading="lazy" />
</a>
{% empty %}
<p>No drop campaigns with emotes found.</p>
{% endfor %}
</div>
{% endblock content %}

View file

@ -0,0 +1,150 @@
{% extends "base.html" %}
{% load static %}
{% block title %}
{{ reward_campaign.name }}
{% endblock title %}
{% block content %}
<!-- Campaign Title -->
{% if reward_campaign.brand %}
<h1 id="campaign-title">{{ reward_campaign.brand }}: {{ reward_campaign.name }}</h1>
{% else %}
<h1 id="campaign-title">{{ reward_campaign.name }}</h1>
{% endif %}
<!-- Back to list link -->
<p>
<a href="{% url 'twitch:reward_campaign_list' %}">← Back to Reward Campaigns</a>
</p>
<!-- RSS Feeds -->
<div style="margin-bottom: 1rem;">
<a href="{% url 'twitch:reward_campaign_feed' %}"
style="margin-right: 1rem"
title="RSS feed for all reward campaigns">RSS feed for all reward campaigns</a>
</div>
<!-- Campaign Summary -->
{% if reward_campaign.summary %}<p id="campaign-summary">{{ reward_campaign.summary|linebreaksbr }}</p>{% endif %}
<!-- Campaign Status -->
<h5>Status</h5>
<table>
<tbody>
<tr>
<td>
<strong>Status:</strong>
</td>
<td>
{% if is_active %}
Active
{% elif reward_campaign.starts_at > now %}
Upcoming
{% else %}
Expired
{% endif %}
</td>
</tr>
<tr>
<td>
<strong>Starts:</strong>
</td>
<td>
<time datetime="{{ reward_campaign.starts_at|date:'c' }}"
title="{{ reward_campaign.starts_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.starts_at|date:"M d, Y H:i" }}
</time>
</td>
</tr>
<tr>
<td>
<strong>Ends:</strong>
</td>
<td>
<time datetime="{{ reward_campaign.ends_at|date:'c' }}"
title="{{ reward_campaign.ends_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.ends_at|date:"M d, Y H:i" }}
</time>
</td>
</tr>
{% if reward_campaign.game %}
<tr>
<td>
<strong>Game:</strong>
</td>
<td>
<a href="{% url 'twitch:game_detail' reward_campaign.game.twitch_id %}">{{ reward_campaign.game.display_name }}</a>
</td>
</tr>
{% elif reward_campaign.is_sitewide %}
<tr>
<td>
<strong>Type:</strong>
</td>
<td>Site-wide reward campaign</td>
</tr>
{% endif %}
</tbody>
</table>
<!-- Instructions -->
{% if reward_campaign.instructions %}
<h5>Instructions</h5>
<p>{{ reward_campaign.instructions|linebreaksbr }}</p>
{% endif %}
<!-- Actions -->
{% if reward_campaign.external_url or reward_campaign.about_url %}
<p>
{% if reward_campaign.external_url %}
<a href="{{ reward_campaign.external_url }}"
target="_blank"
rel="noopener noreferrer">Claim Reward →</a>
{% endif %}
{% if reward_campaign.about_url %}
<a href="{{ reward_campaign.about_url }}"
target="_blank"
rel="noopener noreferrer">Learn More →</a>
{% endif %}
</p>
{% endif %}
<!-- Metadata -->
<h5>Campaign Information</h5>
<table>
<tbody>
{% if reward_campaign.brand %}
<tr>
<td>
<strong>Brand:</strong>
</td>
<td>{{ reward_campaign.brand }}</td>
</tr>
{% endif %}
<tr>
<td>
<strong>Twitch ID:</strong>
</td>
<td>
<code>{{ reward_campaign.twitch_id }}</code>
</td>
</tr>
<tr>
<td>
<strong>Added to tracker:</strong>
</td>
<td>
<time datetime="{{ reward_campaign.added_at|date:'c' }}"
title="{{ reward_campaign.added_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.added_at|date:"M d, Y H:i" }}
</time>
</td>
</tr>
<tr>
<td>
<strong>Last updated:</strong>
</td>
<td>
<time datetime="{{ reward_campaign.updated_at|date:'c' }}"
title="{{ reward_campaign.updated_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.updated_at|date:"M d, Y H:i" }}
</time>
</td>
</tr>
</tbody>
</table>
<hr />
{{ campaign_data|safe }}
{% endblock content %}

View file

@ -0,0 +1,145 @@
{% extends "base.html" %}
{% load static %}
{% block title %}
Reward Campaigns - Twitch Drops Tracker
{% endblock title %}
{% block content %}
<h1 id="page-title">Reward Campaigns (Quest Rewards)</h1>
<p>Browse all available quest reward campaigns</p>
<!-- RSS Feeds -->
<div style="margin-bottom: 1rem;">
<a href="{% url 'twitch:reward_campaign_feed' %}"
style="margin-right: 1rem"
title="RSS feed for all reward campaigns">RSS feed for all reward campaigns</a>
</div>
<!-- Filter Form -->
<form id="filter-form"
method="get"
action="{% url 'twitch:reward_campaign_list' %}">
<label for="game">Game:</label>
<select id="game" name="game">
<option value="">All Games</option>
{% for game in games %}
<option value="{{ game.twitch_id }}"
{% if selected_game == game.twitch_id %}selected{% endif %}>
{{ game.display_name|default:game.name|default:game.slug|default:game.twitch_id }}
</option>
{% endfor %}
</select>
<label for="status">Status:</label>
<select id="status" name="status">
<option value="">All Statuses</option>
{% for status in status_options %}
<option value="{{ status }}"
{% if selected_status == status %}selected{% endif %}>{{ status|title }}</option>
{% endfor %}
</select>
<button id="apply-filters-button" type="submit">Apply Filters</button>
</form>
{% if reward_campaigns %}
<h5>Active Reward Campaigns</h5>
<table>
<tbody>
{% for campaign in reward_campaigns %}
{% if campaign.starts_at <= now and campaign.ends_at >= now %}
<tr id="reward-campaign-{{ campaign.twitch_id }}">
<td>
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">
{% if campaign.brand %}
{{ campaign.brand }}: {{ campaign.name }}
{% else %}
{{ campaign.name }}
{% endif %}
</a>
{% if campaign.summary %}
<br />
<small>{{ campaign.summary }}</small>
{% endif %}
</td>
<td>
{% if campaign.game %}
<a href="{% url 'twitch:game_detail' campaign.game.twitch_id %}">{{ campaign.game.display_name }}</a>
{% elif campaign.is_sitewide %}
Site-wide
{% endif %}
</td>
<td>
<span title="{{ campaign.ends_at|date:'M d, Y H:i' }}">Ends in {{ campaign.ends_at|timeuntil }}</span>
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
<h5>Upcoming Reward Campaigns</h5>
<table>
<tbody>
{% for campaign in reward_campaigns %}
{% if campaign.starts_at > now %}
<tr id="reward-campaign-{{ campaign.twitch_id }}">
<td>
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">
{% if campaign.brand %}
{{ campaign.brand }}: {{ campaign.name }}
{% else %}
{{ campaign.name }}
{% endif %}
</a>
{% if campaign.summary %}
<br />
<small>{{ campaign.summary }}</small>
{% endif %}
</td>
<td>
{% if campaign.game %}
<a href="{% url 'twitch:game_detail' campaign.game.twitch_id %}">{{ campaign.game.display_name }}</a>
{% elif campaign.is_sitewide %}
Site-wide
{% endif %}
</td>
<td>
<span title="Starts on {{ campaign.starts_at|date:'M d, Y H:i' }}">Starts in {{ campaign.starts_at|timeuntil }}</span>
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
<h5>Past Reward Campaigns</h5>
<table>
<tbody>
{% for campaign in reward_campaigns %}
{% if campaign.ends_at < now %}
<tr id="reward-campaign-{{ campaign.twitch_id }}">
<td>
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">
{% if campaign.brand %}
{{ campaign.brand }}: {{ campaign.name }}
{% else %}
{{ campaign.name }}
{% endif %}
</a>
{% if campaign.summary %}
<br />
<small>{{ campaign.summary }}</small>
{% endif %}
</td>
<td>
{% if campaign.game %}
<a href="{% url 'twitch:game_detail' campaign.game.twitch_id %}">{{ campaign.game.display_name }}</a>
{% elif campaign.is_sitewide %}
Site-wide
{% endif %}
</td>
<td>
<span title="Ended on {{ campaign.ends_at|date:'M d, Y H:i' }}">{{ campaign.ends_at|timesince }} ago</span>
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
{% else %}
<p>No reward campaigns found.</p>
{% endif %}
{% endblock content %}

View file

@ -5,7 +5,7 @@
{% block content %}
<div class="container" id="search-results-container">
<h1 id="page-title">Search Results for "{{ query }}"</h1>
{% if not results.organizations and not results.games and not results.campaigns and not results.drops and not results.benefits %}
{% if not results.organizations and not results.games and not results.campaigns and not results.drops and not results.benefits and not results.reward_campaigns %}
<p id="no-results">No results found.</p>
{% else %}
{% if results.organizations %}
@ -68,6 +68,20 @@
{% endfor %}
</ul>
{% endif %}
{% if results.reward_campaigns %}
<h2 id="reward-campaigns-header">Reward Campaigns</h2>
<ul id="reward-campaigns-list">
{% for campaign in results.reward_campaigns %}
<li id="reward-campaign-{{ campaign.twitch_id }}">
{% if campaign.brand %}
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">{{ campaign.brand }}: {{ campaign.name }}</a>
{% else %}
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">{{ campaign.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endif %}
</div>
{% endblock content %}