Restructure files, update README.md, add babel.cfg to source control

This commit is contained in:
Flare Starfall 2026-03-08 10:28:49 +01:00
parent 30309ca0e3
commit 787e26e5b3
20 changed files with 8583 additions and 162 deletions

1
.gitignore vendored
View File

@ -176,5 +176,4 @@ cython_debug/
# ---> StarfallBot # ---> StarfallBot
config.toml config.toml
/babel.*
web/static/css/ web/static/css/

View File

@ -1,3 +1,5 @@
# StarfallBot # Starfall
A bot created for fun. Contains features such as quotes, reading Discord and Twitch chats, and integrating a web overlay. An all-in-one project: Website, Discord bot, Matrix bot, and Twitch bot (as well as database etc. etc.), all in one convenient package.
This is a hobby project; progress will be slow.

5
babel.cfg Normal file
View File

@ -0,0 +1,5 @@
[jinja2: **/templates/**.jinja]
extensions=webassets.ext.jinja2.AssetsExtension
silent=false
[python: **.py]

View File

@ -72,8 +72,8 @@ class WebUI:
"bootstrap/bootstrap-utilities.scss", "bootstrap/bootstrap-utilities.scss",
"bootstrap/bootstrap-reboot.scss", "bootstrap/bootstrap-reboot.scss",
"bootstrap/bootstrap-grid.scss", "bootstrap/bootstrap-grid.scss",
"fonts.scss", "bootstrap/bootstrap-icons.scss",
"main.scss", "starfall/main.scss",
filters="libsass", filters="libsass",
output="css/main.css", output="css/main.css",
depends=["**/*.scss"], depends=["**/*.scss"],

View File

@ -1,3 +1,4 @@
from datetime import datetime
from typing import Any from typing import Any
from flask import Blueprint, Flask, render_template from flask import Blueprint, Flask, render_template
@ -24,11 +25,14 @@ class MainBlueprint(BaseBlueprint):
def imprint(self): def imprint(self):
self._log_access() self._log_access()
lastmod = datetime.fromisoformat("2025-05-11T16:00+01:00")
return render_template( return render_template(
"imprint.jinja", "imprint.jinja",
assets=self.assets, assets=self.assets,
route=self._route(), route=self._route(),
date=self.date, date=self.date,
lastmod=lastmod,
) )
def not_found(self, e: Any): def not_found(self, e: Any):

Binary file not shown.

0
web/static/favicon.png → web/static/img/favicon.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

0
web/static/logo.png → web/static/img/logo.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
$font-family-sans-serif: 'Overpass', system-ui, -apple-system, sans-serif;
$font-size-base: 18px;
$heading-font-family: 'Exo 2', sans-serif;
$heading-font-sizes: (
h1: 4rem,
h2: 2rem,
h3: 1.5rem,
h4: 1rem,
h5: 0.875rem,
h6: 0.75rem
);
$heading-breakpoints: (
'md': 4 / 5,
'sm': 2 / 3
);

View File

@ -1,45 +0,0 @@
@use "sass:map";
@import 'bootstrap/functions';
@import 'bootstrap/variables';
@import 'bs-overrides';
[data-bs-theme="light"] {
--siteBackground: linear-gradient(to bottom right, rgb(80, 160, 220), rgb(114, 165, 255));
}
[data-bs-theme="dark"] {
--siteBackground: linear-gradient(to bottom right, rgb(70, 81, 110), rgb(16, 20, 60));
}
body {
background: var(--siteBackground);
background-repeat: no-repeat;
background-attachment: fixed;
@each $tag, $size in $heading-font-sizes {
#{$tag} {
font: {
family: $heading-font-family;
size: $size;
weight: bold;
}
small {
font-size: calc(2em / 3);
}
@each $breakpoint, $modifier in $heading-breakpoints {
@media (max-width: #{map-get($grid-breakpoints, $breakpoint)}) {
font-size: $size * $modifier;
}
}
}
}
.navbar-brand img {
max-width: 40px;
height: auto;
transform: rotate(340deg);
}
}

View File

@ -0,0 +1,12 @@
$font-family-sans-serif: 'Overpass', system-ui, -apple-system, sans-serif;
$font-size-base: 18px;
[data-bs-theme="light"] {
--site-background: linear-gradient(to bottom right, rgb(80, 160, 220), rgb(114, 165, 255));
--nav-background: rgba(211, 164, 255, 0.3);
}
[data-bs-theme="dark"] {
--site-background: linear-gradient(to bottom right, rgb(70, 81, 110), rgb(16, 20, 60));
--nav-background: rgba(28, 29, 54, 0.4);
}

View File

@ -0,0 +1,37 @@
$heading-font-family: 'Exo 2', sans-serif;
$heading-font-sizes: (
h1: 4rem,
h2: 2rem,
h3: 1.5rem,
h4: 1rem,
h5: 0.875rem,
h6: 0.75rem
);
$heading-breakpoints: (
'md': 4 / 5,
'sm': 2 / 3
);
@mixin headings {
@each $tag, $size in $heading-font-sizes {
#{$tag} {
font: {
family: $heading-font-family;
size: $size;
weight: bold;
}
small {
font-size: calc(2em / 3);
}
@each $breakpoint, $modifier in $heading-breakpoints {
@media (max-width: #{map-get($grid-breakpoints, $breakpoint)}) {
font-size: $size * $modifier;
}
}
}
}
}

View File

@ -0,0 +1,13 @@
@mixin site-backgrounds(){
background: var(--site-background);
background-repeat: no-repeat;
background-attachment: fixed;
&>header {
background: var(--nav-background);
}
&>footer {
background: var(--nav-background);
}
}

View File

@ -0,0 +1,11 @@
$site-margin-top: 8px;
$site-margin-bottom: 48px;
@mixin site-margins() {
#content {
margin: {
top: $site-margin-top;
bottom: $site-margin-bottom;
}
}
}

View File

@ -0,0 +1,23 @@
@use "sass:map";
@import '../bootstrap/functions';
@import '../bootstrap/variables';
@import 'bootstrap-overrides';
@import 'fonts';
@import 'headings';
@import 'site-backgrounds';
@import 'site-margins';
body {
@include headings;
@include site-backgrounds;
@include site-margins;
.navbar-brand img {
max-width: 36px;
height: auto;
transform: rotate(340deg);
}
}

View File

@ -3,14 +3,14 @@
<head> <head>
{# %block title: Page title. #} {# %block title: Page title. #}
<title>{%block title%}{{_('site.title.prefix_when_empty')}}{%endblock%} &ndash; {{_('site.title')}}</title> <title>{%block title%}{{_('base.meta.title.empty')}}{%endblock%} &ndash; {{_('base.meta.title.site')}}</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{_('site.description')}}" /> <meta name="description" content="{{_('base.meta.description')}}" />
<link rel="icon" href="{{url_for('static', filename='favicon.png')}}" /> <link rel="icon" href="{{url_for('static', filename='img/favicon.png')}}" />
<link rel="stylesheet" type="text/css" href="{{assets['scss'].urls()|first}}" /> <link rel="stylesheet" type="text/css" href="{{assets['scss'].urls()|first}}" />
{# %block head: Additional stylesheets/scripts for page. #} {# %block head: Additional stylesheets/scripts for page. #}
@ -18,17 +18,17 @@
</head> </head>
<body> <body>
<a class="visually-hidden-focusable" href="#content">{{_('a11y.skip_to_content')}}</a> <a class="visually-hidden-focusable" href="#content">{{_('base.label.skip_to_content')}}</a>
<header class="sticky-top"> <header class="sticky-top">
<nav class="navbar navbar-expand-md"> <nav class="navbar navbar-expand-md">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="{{url_for('static', filename='logo.png')}}" alt="{{_('site.logo.alt')}}" /> <img src="{{url_for('static', filename='img/logo.png')}}" alt="{{_('base.label.logo_alt')}}" />
</a> </a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="{{_('site.nav.label')}}"> aria-expanded="false" aria-label="{{_('base.label.toggle_nav')}}">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
@ -41,17 +41,19 @@
<li class="nav-menu dropdown"> <li class="nav-menu dropdown">
<button id="theme" class="btn btn-link nav-link dropdown-toggle" type="button" <button id="theme" class="btn btn-link nav-link dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-expanded="false">Theme</button> data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-circle-half"></i>
</button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li> <li>
<button class="dropdown-item" type="button" data-bs-theme-value="light"> <button class="dropdown-item" type="button" data-bs-theme-value="light">
Light <i class="bi bi-brightness-high-fill"></i>&nbsp;{{_('base.label.theme.light')}}
</button> </button>
<button class="dropdown-item" type="button" data-bs-theme-value="dark"> <button class="dropdown-item" type="button" data-bs-theme-value="dark">
Dark <i class="bi bi-brightness-alt-low"></i>&nbsp;{{_('base.label.theme.dark')}}
</button> </button>
<button class="dropdown-item" type="button" data-bs-theme-value="auto"> <button class="dropdown-item" type="button" data-bs-theme-value="auto">
Auto <i class="bi bi-circle-half"></i>&nbsp;{{_('base.label.theme.auto')}}
</button> </button>
</li> </li>
</ul> </ul>
@ -77,7 +79,7 @@
<footer class="fixed-bottom"> <footer class="fixed-bottom">
<nav class="navbar"> <nav class="navbar">
<div class="container-fluid"> <div class="container-fluid">
<small>{{_('site.copy')}} {{date.year}}</small> <small>{{_('base.label.copy')}} {{date.year}}</small>
<ul class="navbar-nav"> <ul class="navbar-nav">
{%include 'components/nav_foot.jinja'%} {%include 'components/nav_foot.jinja'%}
</ul> </ul>

View File

@ -1,100 +1,39 @@
{%extends 'base.jinja'%} {%extends 'base.jinja'%}
{%block title%}Imprint &amp; Privacy{%endblock%} {%block title%}{{_('page.imprint.title')}}{%endblock%}
{%block body%} {%block body%}
<div class="container" id="content"> <div class="container" id="content">
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<h1 class="mb-4">Imprint &amp; Privacy</h1> <h1 class="mb-4">{{_('page.imprint.title')}}</h1>
<section class="card mb-2"> <section class="card mb-2">
<h2 class="card-header">In short</h2> <h2 class="card-header">{{_('page.imprint.card.title')}}</h2>
<div class="card-body"> <div class="card-body">
<p>The only thing this site processes is your site visit, stored for no longer than 7 days unless <p>{{_('page.imprint.card.body')}}</p>
absolutely necessary. This data is only ever accessed by one person (this website's creator), and <p><strong>{{_('page.imprint.card.lastmodified')}} {{lastmod|dateformat}}</strong></p>
only in cases where reading the server logs is necessary (say, a website outage, or reading the logs
while developing a new part of the website).</p>
<p>This disclaimer is not by itself legally binding. The full privacy statement is found below.</p>
<p><strong>Last modified: May 11, 2025</strong></p>
</div> </div>
</section> </section>
{%for i in range(1,4)%}
<section class="border rounded p-2 mb-2"> <section class="border rounded p-2 mb-2">
<h2>Privacy Policy</h2> <h2>{{_('page.imprint.sec' ~ i ~ '.title')}}</h2>
<p>Personal data (usually referred to just as „data“ below) will only be processed by us to the extent {{_('page.imprint.sec' ~ i ~ '.body')|safe}}
necessary and for the purpose of providing a functional and user-friendly website, including its
contents, and the services offered there.</p>
<p>Per Art. 4 No. 1 of Regulation (EU) 2016/679, i.e. the General Data Protection Regulation (hereinafter
referred to as the „GDPR“), „processing“ refers to any operation or set of operations such as
collection, recording, organization, structuring, storage, adaptation, alteration, retrieval,
consultation, use, disclosure by transmission, dissemination, or otherwise making available, alignment,
or combination, restriction, erasure, or destruction performed on personal data, whether by automated
means or not.</p>
<p>The following privacy policy is intended to inform you in particular about the type, scope, purpose,
duration, and legal basis for the processing of such data either under our own control or in conjunction
with others. We also inform you below about the third-party components we use to optimize our website
and improve the user experience which may result in said third parties also processing data they collect
and control.</p>
<p>Our privacy policy is structured as follows:</p>
<p>I. Information about us as controllers of your data<br>II. The rights of users and data subjects<br>III.
Information about the data processing</p>
</section>
<section class="border rounded p-2 mb-2">
<h2>I. Information about us as controllers of your data</h2>
<p>The party responsible for this website (the „controller“) for purposes of data protection law
is:<br>Robert Bäs-Fischlmair a.k.a. Flare Starfall<br>Phone: +43 (0) 677 62890651<br>Email: <a
href="mailto:flare@theflare.at">flare@theflare.at</a></p>
</section>
<section class="border rounded p-2 mb-2">
<h2>II. The rights of users and data subjects</h2>
<p>With regard to the data processing to be described in more detail below, users and data subjects have the
right</p>
<ul>
<li>to confirmation of whether data concerning them is being processed, information about the data being
processed, further information about the nature of the data processing, and copies of the data (cf.
also Art. 15 GDPR);</li>
<li>to correct or complete incorrect or incomplete data (cf. also Art. 16 GDPR);</li>
<li>to the immediate deletion of data concerning them (cf. also Art. 17 DSGVO), or, alternatively, if
further processing is necessary as stipulated in Art. 17 Para. 3 GDPR, to restrict said processing
per Art. 18 GDPR; </li>
<li>to receive copies of the data concerning them and/or provided by them and to have the same
transmitted to other providers/controllers (cf. also Art. 20 GDPR);</li>
<li>to file complaints with the supervisory authority if they believe that data concerning them is being
processed by the controller in breach of data protection provisions (see also Art. 77 GDPR).</li>
</ul>
<p>In addition, the controller is obliged to inform all recipients to whom it discloses data of any such
corrections, deletions, or restrictions placed on processing the same per Art. 16, 17 Para. 1, 18 GDPR.
However, this obligation does not apply if such notification is impossible or involves a
disproportionate effort. Nevertheless, users have a right to information about these recipients.</p>
<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have the right to object to the
controller&apos;s future processing of their data pursuant to Art. 6 Para. 1 lit. f) GDPR. In
particular, an objection to data processing for the purpose of direct advertising is
permissible.</strong></p>
</section>
<section class="border rounded p-2 mb-2">
<h2>III. Information about the data processing</h2>
<p>Your data processed when using our website will be deleted or blocked as soon as the purpose for its
storage ceases to apply, provided the deletion of the same is not in breach of any statutory storage
obligations or unless otherwise stipulated below.</p>
<h3>Server data</h3>
<p>For technical reasons, the following data sent by your internet browser to us or to our server provider
will be collected, especially to ensure a secure and stable website: These server log files record the
type and version of your browser, operating system, the webpages on our site visited, and the date and
time of your visit. The IP address from which you visited our site is additionally recorded if an error
occurs.</p>
<p>The data thus collected will be temporarily stored, but not in association with any other of your data.
</p>
<p>The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. Our legitimate interest lies in the
improvement, stability, functionality, and security of our website.</p>
<p>The data will be deleted within no more than seven days, unless continued storage is required for
evidentiary purposes. In which case, all or part of the data will be excluded from deletion until the
investigation of the relevant incident is finally resolved.</p>
</section> </section>
{%endfor%}
<section> <section>
<p><small>Based on the <a href="https://www.generator-datenschutzerklärung.de" target="_blank" <p><small>{{_('page.imprint.legal')}}</small></p>
rel="noreferrer noopener">Model Data Protection Statement</a> from the <a
href="https://www.ratgeberrecht.eu/" target="_blank" rel="noreferrer noopener">Anwaltskanzlei
Weiß &amp; Partner</a>.<br>Edited to provide details of the data controller, and to detail
exactly what server data is stored.</small></p>
</section> </section>
</div> </div>
</div> </div>
{%endblock%} {%endblock%}
{%block translations%}
{# Unused block, used to tell pybabel what dynamic translations exist on this page. #}
{{_('page.imprint.sec1.title')}}
{{_('page.imprint.sec2.title')}}
{{_('page.imprint.sec3.title')}}
{{_('page.imprint.sec4.title')}}
{{_('page.imprint.sec1.body')}}
{{_('page.imprint.sec2.body')}}
{{_('page.imprint.sec3.body')}}
{{_('page.imprint.sec4.body')}}
{%endblock%}

View File

@ -0,0 +1,100 @@
{%extends 'base.jinja'%}
{%block title%}Imprint &amp; Privacy{%endblock%}
{%block body%}
<div class="container" id="content">
<div class="d-flex flex-column">
<h1 class="mb-4">Imprint &amp; Privacy</h1>
<section class="card mb-2">
<h2 class="card-header">In short</h2>
<div class="card-body">
<p>The only thing this site processes is your site visit, stored for no longer than 7 days unless
absolutely necessary. This data is only ever accessed by one person (this website's creator), and
only in cases where reading the server logs is necessary (say, a website outage, or reading the logs
while developing a new part of the website).</p>
<p>This disclaimer is not by itself legally binding. The full privacy statement is found below.</p>
<p><strong>Last modified: May 11, 2025</strong></p>
</div>
</section>
<section class="border rounded p-2 mb-2">
<h2>Privacy Policy</h2>
<p>Personal data (usually referred to just as „data“ below) will only be processed by us to the extent
necessary and for the purpose of providing a functional and user-friendly website, including its
contents, and the services offered there.</p>
<p>Per Art. 4 No. 1 of Regulation (EU) 2016/679, i.e. the General Data Protection Regulation (hereinafter
referred to as the „GDPR“), „processing“ refers to any operation or set of operations such as
collection, recording, organization, structuring, storage, adaptation, alteration, retrieval,
consultation, use, disclosure by transmission, dissemination, or otherwise making available, alignment,
or combination, restriction, erasure, or destruction performed on personal data, whether by automated
means or not.</p>
<p>The following privacy policy is intended to inform you in particular about the type, scope, purpose,
duration, and legal basis for the processing of such data either under our own control or in conjunction
with others. We also inform you below about the third-party components we use to optimize our website
and improve the user experience which may result in said third parties also processing data they collect
and control.</p>
<p>Our privacy policy is structured as follows:</p>
<p>I. Information about us as controllers of your data<br>II. The rights of users and data subjects<br>III.
Information about the data processing</p>
</section>
<section class="border rounded p-2 mb-2">
<h2>I. Information about us as controllers of your data</h2>
<p>The party responsible for this website (the „controller“) for purposes of data protection law
is:<br>Robert Bäs-Fischlmair a.k.a. Flare Starfall<br>Phone: +43 (0) 677 62890651<br>Email: <a
href="mailto:flare@theflare.at">flare@theflare.at</a></p>
</section>
<section class="border rounded p-2 mb-2">
<h2>II. The rights of users and data subjects</h2>
<p>With regard to the data processing to be described in more detail below, users and data subjects have the
right</p>
<ul>
<li>to confirmation of whether data concerning them is being processed, information about the data being
processed, further information about the nature of the data processing, and copies of the data (cf.
also Art. 15 GDPR);</li>
<li>to correct or complete incorrect or incomplete data (cf. also Art. 16 GDPR);</li>
<li>to the immediate deletion of data concerning them (cf. also Art. 17 DSGVO), or, alternatively, if
further processing is necessary as stipulated in Art. 17 Para. 3 GDPR, to restrict said processing
per Art. 18 GDPR; </li>
<li>to receive copies of the data concerning them and/or provided by them and to have the same
transmitted to other providers/controllers (cf. also Art. 20 GDPR);</li>
<li>to file complaints with the supervisory authority if they believe that data concerning them is being
processed by the controller in breach of data protection provisions (see also Art. 77 GDPR).</li>
</ul>
<p>In addition, the controller is obliged to inform all recipients to whom it discloses data of any such
corrections, deletions, or restrictions placed on processing the same per Art. 16, 17 Para. 1, 18 GDPR.
However, this obligation does not apply if such notification is impossible or involves a
disproportionate effort. Nevertheless, users have a right to information about these recipients.</p>
<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have the right to object to the
controller&apos;s future processing of their data pursuant to Art. 6 Para. 1 lit. f) GDPR. In
particular, an objection to data processing for the purpose of direct advertising is
permissible.</strong></p>
</section>
<section class="border rounded p-2 mb-2">
<h2>III. Information about the data processing</h2>
<p>Your data processed when using our website will be deleted or blocked as soon as the purpose for its
storage ceases to apply, provided the deletion of the same is not in breach of any statutory storage
obligations or unless otherwise stipulated below.</p>
<h3>Server data</h3>
<p>For technical reasons, the following data sent by your internet browser to us or to our server provider
will be collected, especially to ensure a secure and stable website: These server log files record the
type and version of your browser, operating system, the webpages on our site visited, and the date and
time of your visit. The IP address from which you visited our site is additionally recorded if an error
occurs.</p>
<p>The data thus collected will be temporarily stored, but not in association with any other of your data.
</p>
<p>The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. Our legitimate interest lies in the
improvement, stability, functionality, and security of our website.</p>
<p>The data will be deleted within no more than seven days, unless continued storage is required for
evidentiary purposes. In which case, all or part of the data will be excluded from deletion until the
investigation of the relevant incident is finally resolved.</p>
</section>
<section>
<p><small>Based on the <a href="https://www.generator-datenschutzerklärung.de" target="_blank"
rel="noreferrer noopener">Model Data Protection Statement</a> from the <a
href="https://www.ratgeberrecht.eu/" target="_blank" rel="noreferrer noopener">Anwaltskanzlei
Weiß &amp; Partner</a>.<br>Edited to provide details of the data controller, and to detail
exactly what server data is stored.</small></p>
</section>
</div>
</div>
{%endblock%}