diff --git a/Spybot2/settings.py b/Spybot2/settings.py index a596d83..fa7aa0b 100644 --- a/Spybot2/settings.py +++ b/Spybot2/settings.py @@ -66,6 +66,8 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.forms', + 'bootstrap4', ] MIDDLEWARE = [ @@ -217,4 +219,12 @@ # 'root': { # 'handlers': ['console'], # } -# } \ No newline at end of file +# } + +FORM_RENDERER = 'spybot.forms.CustomFormRenderer' + +BOOTSTRAP4 = { + 'include_jquery': False, + 'javascript_in_head': False, + 'label_class': 'form-label', +} \ No newline at end of file diff --git a/frontend/main.js b/frontend/main.js index 5dbc186..0f6e57b 100644 --- a/frontend/main.js +++ b/frontend/main.js @@ -5,6 +5,7 @@ import * as passkeys from './passkeys'; import "@tabler/core/dist/css/tabler.min.css" import "@tabler/core/dist/css/tabler-vendors.min.css" +import './modal' window.passkeys = passkeys; diff --git a/frontend/modal.js b/frontend/modal.js new file mode 100644 index 0000000..e140aa4 --- /dev/null +++ b/frontend/modal.js @@ -0,0 +1,20 @@ + +const modal = new bootstrap.Modal(document.querySelector("#modal"), {}); + +htmx.on("htmx:afterSwap", (e) => { + if (e.detail.target.id === "dialog") { + modal.show() + } +}) + +htmx.on("htmx:beforeSwap", (e) => { + // Empty response targeting #dialog => hide the modal + if (e.detail.target.id === "dialog" && !e.detail.xhr.response) { + modal.hide() + e.detail.shouldSwap = false + } +}) + +htmx.on("hidden.bs.modal", () => { + document.getElementById("dialog").innerHTML = "" +}) diff --git a/poetry.lock b/poetry.lock index d8fef9d..2f44e78 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "asgiref" version = "3.8.1" description = "ASGI specs, helper code, and adapters" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -18,6 +19,7 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "bcrypt" version = "4.1.3" description = "Modern password hashing for your software and your servers" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -54,10 +56,33 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "certifi" version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -69,6 +94,7 @@ files = [ name = "cffi" version = "1.16.0" description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -133,6 +159,7 @@ pycparser = "*" name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -232,6 +259,7 @@ files = [ name = "cryptography" version = "42.0.8" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -286,6 +314,7 @@ test-randomorder = ["pytest-randomly"] name = "django" version = "4.2.13" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -302,10 +331,27 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""} argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] +[[package]] +name = "django-bootstrap4" +version = "24.3" +description = "Django extensions by Zostera" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_bootstrap4-24.3-py3-none-any.whl", hash = "sha256:b555d87740a571036f100ad6026b1f62aabcb913404fb7f08f521881019b14bc"}, + {file = "django_bootstrap4-24.3.tar.gz", hash = "sha256:819bc0ba7b25fcdeb12eb04353962436dbe95b228ba4cf4b49f5d3fee53692e1"}, +] + +[package.dependencies] +beautifulsoup4 = ">=4.8.0" +Django = ">=4.1" + [[package]] name = "django-crontab" version = "0.7.1" description = "dead simple crontab powered job scheduling for django" +category = "main" optional = false python-versions = "*" files = [ @@ -319,6 +365,7 @@ Django = ">=1.8" name = "django-environ" version = "0.11.2" description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application." +category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -327,14 +374,15 @@ files = [ ] [package.extras] -develop = ["coverage[toml] (>=5.0a4)", "furo (>=2021.8.17b43,<2021.9.dev0)", "pytest (>=4.6.11)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] -docs = ["furo (>=2021.8.17b43,<2021.9.dev0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +develop = ["coverage[toml] (>=5.0a4)", "furo (>=2021.8.17b43,<2021.9.0)", "pytest (>=4.6.11)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +docs = ["furo (>=2021.8.17b43,<2021.9.0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"] [[package]] name = "docopt" version = "0.6.2" description = "Pythonic argument parser, that will make you smile" +category = "main" optional = false python-versions = "*" files = [ @@ -345,6 +393,7 @@ files = [ name = "fido2" version = "1.1.3" description = "FIDO2/WebAuthn library for implementing clients and servers." +category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -362,6 +411,7 @@ pcsc = ["pyscard (>=1.9,<3)"] name = "idna" version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -373,6 +423,7 @@ files = [ name = "lxml" version = "5.2.2" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -531,6 +582,7 @@ source = ["Cython (>=3.0.10)"] name = "mysqlclient" version = "2.2.4" description = "Python interface to MySQL" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -549,6 +601,7 @@ files = [ name = "num2words" version = "0.5.13" description = "Modules to convert numbers to words. Easily extensible." +category = "main" optional = false python-versions = "*" files = [ @@ -563,6 +616,7 @@ docopt = ">=0.6.2" name = "paramiko" version = "3.4.0" description = "SSH2 protocol library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -584,6 +638,7 @@ invoke = ["invoke (>=2.0)"] name = "pycparser" version = "2.22" description = "C parser in Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -595,6 +650,7 @@ files = [ name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -621,6 +677,7 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "requests" version = "2.32.3" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -638,10 +695,23 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "soupsieve" +version = "2.6" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, +] + [[package]] name = "sqlparse" version = "0.5.0" description = "A non-validating SQL parser." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -657,6 +727,7 @@ doc = ["sphinx"] name = "ts3" version = "2.0.0b3" description = "TS3 Server Query API and TS3 Client Query API" +category = "main" optional = false python-versions = "*" files = [ @@ -671,6 +742,7 @@ paramiko = "*" name = "tzdata" version = "2024.1" description = "Provider of IANA time zone data" +category = "main" optional = false python-versions = ">=2" files = [ @@ -682,6 +754,7 @@ files = [ name = "ua-parser" version = "0.18.0" description = "Python port of Browserscope's user agent parser" +category = "main" optional = false python-versions = "*" files = [ @@ -693,6 +766,7 @@ files = [ name = "unittest-xml-reporting" version = "3.2.0" description = "unittest-based test runner with Ant/JUnit like XML reporting." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -707,6 +781,7 @@ lxml = "*" name = "urllib3" version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -724,6 +799,7 @@ zstd = ["zstandard (>=0.18.0)"] name = "user-agents" version = "2.2.0" description = "A library to identify devices (phones, tablets) and their capabilities by parsing browser user agent strings." +category = "main" optional = false python-versions = "*" files = [ @@ -737,4 +813,4 @@ ua-parser = ">=0.10.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "9c3d9437a5c87b270c756d42adc7902256660f66fd34b07bd58aa2266b4d3ca6" +content-hash = "2170d1d9041752540c0498ec3272debfe83af2cce00af973754e89aa34213943" diff --git a/pyproject.toml b/pyproject.toml index 842f732..f937a90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ num2words = "^0.5.12" requests = "^2.31.0" fido2 = "^1.1.3" user-agents = "^2.2.0" +django-bootstrap4 = "^24.3" [tool.poetry.group.test.dependencies] diff --git a/spybot/forms.py b/spybot/forms.py index f7631ac..77588d6 100644 --- a/spybot/forms.py +++ b/spybot/forms.py @@ -1,5 +1,12 @@ from django import forms +from django.core.exceptions import ValidationError +from django.forms.renderers import TemplatesSetting +from spybot.remote.steam_api import get_steam_user_playing_info, get_steam_account_info + + +class CustomFormRenderer(TemplatesSetting): + form_template_name = 'spybot/form_snippet.html' class TimeRangeForm(forms.Form): RANGES = ( @@ -25,3 +32,42 @@ def clean(self): cleaned_data[name] = field.initial return cleaned_data + + +class AddSteamIDForm(forms.Form): + steamid = forms.CharField(label="Account ID (what comes after https://steamcommunity.com/profiles/)", initial="123456", required=True) + name = forms.CharField(label="Account name") + + def clean_steamid(self): + cleaned_data = super().clean() + steamid = cleaned_data.get("steamid") + name = cleaned_data.get("name") + + steam_info = get_steam_account_info(steamid) + if steam_info is None: + raise ValidationError("Can't load steam information for this user") + + return cleaned_data + + + """ + +