Update webhook page

This commit is contained in:
2024-07-06 23:29:39 +02:00
parent 9da33b402c
commit f37975d94a
15 changed files with 122 additions and 127 deletions

View File

@ -21,9 +21,9 @@ repos:
- id: end-of-file-fixer
- id: mixed-line-ending
- id: name-tests-test
args: [--pytest-test-first]
args: [ --pytest-test-first ]
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
args: [ --markdown-linebreak-ext=md ]
exclude_types:
- "html"
@ -32,22 +32,22 @@ repos:
rev: "1.19.0"
hooks:
- id: django-upgrade
args: [--target-version, "5.1"]
args: [ --target-version, "5.1" ]
# Run Pyupgrade on all Python files. This will upgrade the code to Python 3.12.
- repo: https://github.com/asottile/pyupgrade
rev: v3.16.0
hooks:
- id: pyupgrade
args: ["--py312-plus"]
args: [ "--py312-plus" ]
# An extremely fast Python linter and formatter.
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
rev: v0.5.1
hooks:
- id: ruff-format
- id: ruff
args: ["--fix", "--exit-non-zero-on-fix"]
args: [ "--fix", "--exit-non-zero-on-fix" ]
# Static checker for GitHub Actions workflow files.
- repo: https://github.com/rhysd/actionlint

52
.vscode/launch.json vendored
View File

@ -1,28 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Django: Runserver",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"runserver",
"--nothreading"
],
"django": true,
"justMyCode": true
},
{
"name": "python manage.py scrape_twitch",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"scrape_twitch"
],
"django": true,
"justMyCode": true
}
]
"version": "0.2.0",
"configurations": [
{
"name": "Django: Runserver",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"runserver",
"--nothreading"
],
"django": true,
"justMyCode": true
},
{
"name": "python manage.py scrape_twitch",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"scrape_twitch"
],
"django": true,
"justMyCode": true
}
]
}

30
.vscode/settings.json vendored
View File

@ -1,17 +1,17 @@
{
"cSpell.words": [
"allauth",
"appendonly",
"asgiref",
"forloop",
"logdir",
"memlock",
"networkidle",
"PGID",
"PUID",
"requirepass",
"socialaccount",
"ttvdrops",
"ulimits"
]
"cSpell.words": [
"allauth",
"appendonly",
"asgiref",
"forloop",
"logdir",
"memlock",
"networkidle",
"PGID",
"PUID",
"requirepass",
"socialaccount",
"ttvdrops",
"ulimits"
]
}

View File

@ -8,7 +8,6 @@ from platformdirs import user_data_dir
load_dotenv(dotenv_path=find_dotenv(), verbose=True)
DATA_DIR = Path(
user_data_dir(
appname="TTVDrops",
@ -28,7 +27,6 @@ sentry_sdk.init(
profiles_sample_rate=0.2,
)
BASE_DIR: Path = Path(__file__).resolve().parent.parent
ADMINS: list[tuple[str, str]] = [("Joakim Hellsén", "tlovinator@gmail.com")]
WSGI_APPLICATION = "config.wsgi.application"
@ -89,7 +87,6 @@ MIDDLEWARE: list[str] = [
"simple_history.middleware.HistoryRequestMiddleware",
]
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",

View File

@ -107,16 +107,16 @@
</div>
</div>
<script type="module">
const scrollSpy = new bootstrap.ScrollSpy(document.body, {
target: '.toc'
})
const scrollSpy = new bootstrap.ScrollSpy(document.body, {
target: '.toc'
})
document.body.addEventListener('activate.bs.scrollspy', function (event) {
const activeItem = document.querySelector('.toc .active');
if (activeItem) {
activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
}
});
document.body.addEventListener('activate.bs.scrollspy', function (event) {
const activeItem = document.querySelector('.toc .active');
if (activeItem) {
activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
}
});
</script>
</body>
{% endblock content %}

View File

@ -2,22 +2,27 @@
{% block content %}
<div class="container">
<h1 class="my-4">Add Discord Webhook</h1>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
<div class="mb-3">
{{ form.webhook_url.errors }}
<label for="{{ form.webhook_url.id_for_label }}" class="form-label">{{ form.webhook_url.label }}</label>
<input type="url"
name="webhook_url"
required=""
class="form-control"
aria-describedby="id_webhook_url_helptext"
id="id_webhook_url">
<div class="form-text text-muted">{{ form.webhook_url.help_text }}</div>
</div>
<button type="submit" class="btn btn-primary">Add Webhook</button>
</form>
<div class="card card-body mb-3">
Webhooks will be saved in a cookie and will be sent to the server when you subscribe to a drop.
</div>
<div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
<div class="mb-3">
{{ form.webhook_url.errors }}
<label for="{{ form.webhook_url.id_for_label }}" class="form-label">{{ form.webhook_url.label }}</label>
<input type="url"
name="webhook_url"
required=""
class="form-control"
aria-describedby="id_webhook_url_helptext"
id="id_webhook_url">
<div class="form-text text-muted">{{ form.webhook_url.help_text }}</div>
</div>
<button type="submit" class="btn btn-primary">Add Webhook</button>
</form>
</div>
<h2 class="mt-5">Webhooks</h2>
{% if webhooks %}
<ul class="list-group mt-3">

View File

@ -8,13 +8,13 @@ if TYPE_CHECKING:
from django.http import HttpResponse
@pytest.fixture()
@pytest.fixture
def factory() -> RequestFactory:
"""Factory for creating requests."""
return RequestFactory()
@pytest.mark.django_db()
@pytest.mark.django_db
def test_index_view(client: Client) -> None:
"""Test index view."""
url: str = reverse(viewname="core:index")

View File

@ -6,7 +6,6 @@ from . import views
app_name: str = "core"
urlpatterns: list[URLPattern | URLResolver] = [
path(route="", view=views.index, name="index"),
path(

View File

@ -11,7 +11,6 @@ from django.contrib import messages
from django.http.response import HttpResponse
from django.template.response import TemplateResponse
from django.views.generic import FormView, ListView
from httpx._models import Response
from twitch_app.models import (
DropBenefit,
@ -33,7 +32,6 @@ if TYPE_CHECKING:
logger: logging.Logger = logging.getLogger(__name__)
cache_dir: Path = settings.DATA_DIR / "cache"
cache_dir.mkdir(exist_ok=True, parents=True)
storage = hishel.FileStorage(base_path=cache_dir)
@ -284,7 +282,7 @@ class WebhooksView(FormView):
webhooks.append(webhook)
response: HttpResponse = self.render_to_response(self.get_context_data(form=form))
response.set_cookie(key="webhooks", value=",".join(webhooks), max_age=60 * 60 * 24 * 365)
response.set_cookie(key="webhooks", value=",".join(webhooks), max_age=315360000) # 10 years
messages.success(self.request, "Webhook successfully added.")
return response

View File

@ -6,7 +6,7 @@ services:
restart: always
ulimits:
memlock: -1
command: ["--auth", "Password", "--password", "${GARNET_PASSWORD}", "--storage-tier", "--logdir", "/logs", "--aof", "--port", "6380"]
command: [ "--auth", "Password", "--password", "${GARNET_PASSWORD}", "--storage-tier", "--logdir", "/logs", "--aof", "--port", "6380" ]
ports:
- "6380:6380"
volumes:

View File

@ -8,36 +8,36 @@ lint.select = ["ALL"]
line-length = 119
lint.pydocstyle.convention = "google"
lint.ignore = [
"CPY001", # Missing copyright notice at top of file
"D100", # Checks for undocumented public module definitions.
"D101", # Checks for undocumented public class definitions.
"D102", # Checks for undocumented public method definitions.
"D104", # Missing docstring in public package.
"D105", # Missing docstring in magic method.
"D106", # Checks for undocumented public class definitions, for nested classes.
"ERA001", # Found commented-out code
"FIX002", # Line contains TODO
"COM812", # Checks for the absence of trailing commas.
"ISC001", # Checks for implicitly concatenated strings on a single line.
"DJ001", # Checks nullable string-based fields (like CharField and TextField) in Django models.
"CPY001", # Missing copyright notice at top of file
"D100", # Checks for undocumented public module definitions.
"D101", # Checks for undocumented public class definitions.
"D102", # Checks for undocumented public method definitions.
"D104", # Missing docstring in public package.
"D105", # Missing docstring in magic method.
"D106", # Checks for undocumented public class definitions, for nested classes.
"ERA001", # Found commented-out code
"FIX002", # Line contains TODO
"COM812", # Checks for the absence of trailing commas.
"ISC001", # Checks for implicitly concatenated strings on a single line.
"DJ001", # Checks nullable string-based fields (like CharField and TextField) in Django models.
]
[tool.ruff.lint.per-file-ignores]
"**/tests/**" = [
"ARG", # Unused function args -> fixtures nevertheless are functionally relevant...
"FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize()
"PLR2004", # Magic value used in comparison, ...
"S101", # asserts allowed in tests...
"S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
"ARG", # Unused function args -> fixtures nevertheless are functionally relevant...
"FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize()
"PLR2004", # Magic value used in comparison, ...
"S101", # asserts allowed in tests...
"S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
]
"**/migrations/**" = [
"RUF012", # Checks for mutable default values in class attributes.
"RUF012", # Checks for mutable default values in class attributes.
]
[tool.djlint]
profile = "django"
format_attribute_template_tags = true
ignore="H006" # Img tag should have height and width attributes.
ignore = "H006" # Img tag should have height and width attributes.
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings"

View File

@ -1,5 +1,6 @@
djlint
pip
pre-commit
ruff
pytest
pytest-django
ruff

View File

@ -8,7 +8,6 @@ httpx
pillow
platformdirs
playwright
psycopg[binary]
python-dotenv
sentry-sdk[django]
whitenoise[brotli]

View File

@ -86,9 +86,7 @@ class DropCampaignSchema(Schema):
# http://localhost:8000/api/twitch/organizations
@router.get("/organizations", response=list[OrganizationSchema])
def get_organizations(
request: HttpRequest, # noqa: ARG001
) -> BaseManager[Organization]:
def get_organizations(request: HttpRequest) -> BaseManager[Organization]: # noqa: ARG001
"""Get all organizations."""
return Organization.objects.all()
@ -109,17 +107,13 @@ def get_drop_benefits(request: HttpRequest) -> BaseManager[DropBenefit]: # noqa
# http://localhost:8000/api/twitch/drop_campaigns
@router.get("/drop_campaigns", response=list[DropCampaignSchema])
def get_drop_campaigns(
request: HttpRequest, # noqa: ARG001
) -> BaseManager[DropCampaign]:
def get_drop_campaigns(request: HttpRequest) -> BaseManager[DropCampaign]: # noqa: ARG001
"""Get all drop campaigns."""
return DropCampaign.objects.all()
# http://localhost:8000/api/twitch/time_based_drops
@router.get("/time_based_drops", response=list[TimeBasedDropSchema])
def get_time_based_drops(
request: HttpRequest, # noqa: ARG001
) -> BaseManager[TimeBasedDrop]:
def get_time_based_drops(request: HttpRequest) -> BaseManager[TimeBasedDrop]: # noqa: ARG001
"""Get all time-based drops."""
return TimeBasedDrop.objects.all()

View File

@ -1,3 +1,5 @@
from typing import Literal
import auto_prefetch
from django.db import models
from django.db.models import Value
@ -19,9 +21,9 @@ class Organization(auto_prefetch.Model):
modified_at = models.DateTimeField(blank=True, null=True, auto_now=True)
class Meta(auto_prefetch.Model.Meta):
verbose_name = "Organization"
verbose_name_plural = "Organizations"
ordering = ("name",)
verbose_name: str = "Organization"
verbose_name_plural: str = "Organizations"
ordering: tuple[Literal["name"]] = ("name",)
def __str__(self) -> str:
return self.name or self.id
@ -55,9 +57,9 @@ class Game(auto_prefetch.Model):
history = HistoricalRecords()
class Meta(auto_prefetch.Model.Meta):
verbose_name = "Game"
verbose_name_plural = "Games"
ordering = ("display_name",)
verbose_name: str = "Game"
verbose_name_plural: str = "Games"
ordering: tuple[Literal["display_name"]] = ("display_name",)
def __str__(self) -> str:
return self.display_name or self.slug or self.id
@ -83,9 +85,9 @@ class DropBenefit(auto_prefetch.Model):
history = HistoricalRecords()
class Meta(auto_prefetch.Model.Meta):
verbose_name = "Drop Benefit"
verbose_name_plural = "Drop Benefits"
ordering = ("name",)
verbose_name: str = "Drop Benefit"
verbose_name_plural: str = "Drop Benefits"
ordering: tuple[Literal["name"]] = ("name",)
def __str__(self) -> str:
return f"{self.owner_organization.name} - {self.game.display_name} - {self.name}"
@ -106,9 +108,9 @@ class TimeBasedDrop(auto_prefetch.Model):
history = HistoricalRecords()
class Meta(auto_prefetch.Model.Meta):
verbose_name = "Time-Based Drop"
verbose_name_plural = "Time-Based Drops"
ordering = ("name",)
verbose_name: str = "Time-Based Drop"
verbose_name_plural: str = "Time-Based Drops"
ordering: tuple[Literal["name"]] = ("name",)
def __str__(self) -> str:
return f"{self.benefits.first()} - {self.name}"
@ -145,9 +147,9 @@ class DropCampaign(auto_prefetch.Model):
history = HistoricalRecords()
class Meta(auto_prefetch.Model.Meta):
verbose_name = "Drop Campaign"
verbose_name_plural = "Drop Campaigns"
ordering = ("name",)
verbose_name: str = "Drop Campaign"
verbose_name_plural: str = "Drop Campaigns"
ordering: tuple[Literal["name"]] = ("name",)
def __str__(self) -> str:
return f"{self.owner.name} - {self.game.display_name} - {self.name}"