diff --git a/.idea/strolchibot.iml b/.idea/strolchibot.iml index 53fc3b2169ee9db5a37d2fba3a154a2330a4e243..a69cde905cbf1f07c0a033378a784a2a7a9f90f6 100644 --- a/.idea/strolchibot.iml +++ b/.idea/strolchibot.iml @@ -23,5 +23,11 @@ </component> <component name="TemplatesService"> <option name="TEMPLATE_CONFIGURATION" value="Django" /> + <option name="TEMPLATE_FOLDERS"> + <list> + <option value="$MODULE_DIR$/strolchibot/templates" /> + <option value="$MODULE_DIR$/strolchguru/templates" /> + </list> + </option> </component> </module> \ No newline at end of file diff --git a/clip_updater.py b/clip_updater.py index 9e9c008cf6968098a86d0b3aff783deedd42fac4..b4c53ea36146bc818a13609c08bbe7b0672d6084 100644 --- a/clip_updater.py +++ b/clip_updater.py @@ -1,6 +1,5 @@ import sqlite3 import time -from pprint import pprint import youtube_dl from gql import Client, gql @@ -76,9 +75,6 @@ def save_clips(clips): created_at = node["createdAt"] duration = node["durationSeconds"] - if clip_id == 946839771: - pprint(node) - if clip := get_clip(c, clip_id): c.execute('UPDATE strolchguru_clip SET title = ?, url = ?, embed_url = ?, slug = ?, thumbnail_url = ?, ' 'curator = ?, clip_url = ?, is_published = ?, created_at = ?, duration = ? WHERE clip_id = ?', diff --git a/strolchguru/forms.py b/strolchguru/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..43e355195d61fc2768df34a59c215ff16837e413 --- /dev/null +++ b/strolchguru/forms.py @@ -0,0 +1,19 @@ +from django import forms + + +class ClipSearchForm(forms.Form): + search = forms.CharField(label="Search", required=False) + + def __init__(self, *args, **kwargs): + super(forms.Form, self).__init__(*args, **kwargs) + add_classes(self.fields) + + +def add_classes(fields): + for field_name, field in fields.items(): + if type(field) is forms.fields.BooleanField: + field.widget.attrs['class'] = ' w3-switch ' + field.label_suffix = "" + else: + field.widget.attrs['class'] = ' w3-input' + field.widget.attrs['placeholder'] = field.label diff --git a/strolchguru/migrations/0003_auto_20211114_0026.py b/strolchguru/migrations/0003_auto_20211114_0026.py new file mode 100644 index 0000000000000000000000000000000000000000..71e9355e6c2907c8839df8115108c865d0d17e36 --- /dev/null +++ b/strolchguru/migrations/0003_auto_20211114_0026.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2021-11-13 23:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('strolchguru', '0002_auto_20211024_0019'), + ] + + operations = [ + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ], + ), + migrations.AddField( + model_name='clip', + name='tags', + field=models.ManyToManyField(to='strolchguru.Tag'), + ), + ] diff --git a/strolchguru/migrations/0004_auto_20211114_0047.py b/strolchguru/migrations/0004_auto_20211114_0047.py new file mode 100644 index 0000000000000000000000000000000000000000..38f6cd82637391d98e91433bb2e82aee61af57ce --- /dev/null +++ b/strolchguru/migrations/0004_auto_20211114_0047.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.8 on 2021-11-13 23:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('strolchguru', '0003_auto_20211114_0026'), + ] + + operations = [ + migrations.AlterField( + model_name='clip', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='tag', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/strolchguru/models.py b/strolchguru/models.py index 94abe35fbc6d9d3a6347d9c7a2cdb8a8ddb7eec2..22b7e5190b26112f81f6391b88fe2837b9347d06 100644 --- a/strolchguru/models.py +++ b/strolchguru/models.py @@ -15,3 +15,15 @@ class Clip(models.Model): created_at = models.DateTimeField() is_published = models.BooleanField(default=True) is_downloaded = models.BooleanField(default=False) + tags = models.ManyToManyField("Tag") + + @property + def display_title(self): + if self.custom_title: + return self.custom_title + + return self.title + + +class Tag(models.Model): + name = models.CharField(max_length=100) diff --git a/strolchguru/templates/clips.html b/strolchguru/templates/clips.html new file mode 100644 index 0000000000000000000000000000000000000000..60dcdb47ad27a9244267ffa1178d9b7ced6356ed --- /dev/null +++ b/strolchguru/templates/clips.html @@ -0,0 +1,46 @@ +{% extends 'layout.html' %} + +{% block title %} + <div style="width: 1000px; margin-bottom: 10px; position: absolute; top: 20px; left: 150px;"> + <div style="position:relative;"> + <form method="get"> + {% for field in search_form %} + {{ field }} + {% endfor %} + <div style="position: absolute; right: -48px; top:-1px;"> + <button type="submit" value="" class="w3-btn w3-strolchpink" style="height: 41px;"> + <i class="fas fa-search"></i></button> + </div> + </form> + </div> + </div> +{% endblock %} + +{% block content %} + {% for clip in clips %} + <div class="w3-card clip-card" + style="width: 480px; padding: 10px; box-sizing: content-box; float: left; margin-right: 10px; height:400px; overflow: hidden;"> + <div style="height: 272px; width: 480px; position: relative"> + <a href="{% url "clip" clip.id %}"> + <img src="/media/clips/{{ clip.clip_id }}.jpg"/> + </a> + <overlay + style="position: absolute; left: 0px; top: 0px; color: white; background: rgba(0,0,0,0.6); border-radius: 0.2rem; padding: 5px; margin: 5px;">{{ clip.created_at|date:"j.m.Y H:i" }}</overlay> + <overlay + style="position: absolute; right: 0px; bottom: 0px; color: white; background: rgba(0,0,0,0.6); border-radius: 0.2rem; padding: 5px; margin: 5px;">{{ clip.duration }} + sec + </overlay> + <overlay + style="position: absolute; left: 0px; bottom: 0px; color: white; background: rgba(0,0,0,0.6); border-radius: 0.2rem; padding: 5px; margin: 5px;">{{ clip.curator }}</overlay> + </div> + <p><b>{{ clip.display_title }}</b></p> + <p> + {% for tag in clip.tags.all %} + <a href="{% url "clips" %}?search={{ tag.name }}" style="text-decoration: none;"> + <span class="w3-tag w3-round w3-strolchpink">{{ tag.name }}</span> + </a> + {% endfor %} + </p> + </div> + {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/strolchguru/templates/strolchguru_home.html b/strolchguru/templates/strolchguru_home.html index e3ce41b2bc63d1e87322be380878e457f7d312f8..3e6b29145febd1c61057285150d9e69dc2f7744e 100644 --- a/strolchguru/templates/strolchguru_home.html +++ b/strolchguru/templates/strolchguru_home.html @@ -48,7 +48,7 @@ src="/media/clips/{{ clip.clip_id }}.mp4"> </video> <div id="caption"> - <h1 id="clip_title">#{{ clip.id }} - {{ clip.title }}</h1> + <h1 id="clip_title">#{{ clip.id }} - {{ clip.display_title }}</h1> <p id="clipper">von {{ clip.curator }}</p> </div> </body> diff --git a/strolchguru/urls.py b/strolchguru/urls.py index ced75ad619d7d7d30c13e642ff0767b57a2a617c..445d2879e855d3e09806f49637e8cf3aa153f5db 100644 --- a/strolchguru/urls.py +++ b/strolchguru/urls.py @@ -21,4 +21,5 @@ urlpatterns = [ path('', views.home, name="strolchguru"), path('<int:id>', views.clip, name="clip"), path('<int:id>/json', views.clip_json, name="clip_json"), + path('clips', views.clips, name="clips"), ] diff --git a/strolchguru/views.py b/strolchguru/views.py index da48f9d9c717f38450cd6c0fadd5d79bdf6f88d7..aaac57babf86171aaf0b465d467c303cd76ecad6 100644 --- a/strolchguru/views.py +++ b/strolchguru/views.py @@ -1,9 +1,13 @@ import random +from django.core.paginator import Paginator +from django.db.models import Q from django.forms.models import model_to_dict from django.http import HttpResponse, JsonResponse, Http404 from django.shortcuts import render, get_object_or_404 +from strolchibot.models import TwitchUser +from .forms import ClipSearchForm from .models import Clip @@ -32,3 +36,24 @@ def clip_json(request, id) -> JsonResponse: return JsonResponse(json) except Clip.DoesNotExist: return JsonResponse({"error": "Clip with this id does not exist"}) + + +def clips(request) -> HttpResponse: + clips = Clip.objects.filter(is_downloaded=True).order_by("-created_at") + if not (isinstance(request.user, TwitchUser) and request.user.is_mod and request.user.is_admin): + clips = clips.filter(is_published=True) + + form = ClipSearchForm(request.GET) + # check whether it's valid: + if form.is_valid(): + search = form.cleaned_data["search"] + clips = clips.filter( + Q(title__icontains=search) | Q(curator__icontains=search) | Q(tags__name__icontains=search)) + + else: + clips = None + + paginator = Paginator(clips, 50) + page_obj = paginator.get_page(1) + + return render(request, "clips.html", context={"title": "Clips", "clips": page_obj, "search_form": form}) diff --git a/strolchibot/migrations/0006_auto_20210121_0000.py b/strolchibot/migrations/0006_auto_20210121_0000.py new file mode 100644 index 0000000000000000000000000000000000000000..02036727636cbd8187010608735a8d26b80bbff1 --- /dev/null +++ b/strolchibot/migrations/0006_auto_20210121_0000.py @@ -0,0 +1,16 @@ +# Generated by Django 3.1.2 on 2021-01-20 23:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ('strolchibot', '0005_auto_20210108_0031'), + ] + + operations = [ + migrations.RenameModel( + old_name='TextCommand', + new_name='Command', + ), + ] diff --git a/strolchibot/migrations/0007_auto_20211114_0047.py b/strolchibot/migrations/0007_auto_20211114_0047.py new file mode 100644 index 0000000000000000000000000000000000000000..dac811027269dfe0238026612ef3e44ff33759f1 --- /dev/null +++ b/strolchibot/migrations/0007_auto_20211114_0047.py @@ -0,0 +1,47 @@ +# Generated by Django 3.2.8 on 2021-11-13 23:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('strolchibot', '0006_auto_20210121_0000'), + ] + + operations = [ + migrations.AlterField( + model_name='command', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='config', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='klassenbuch', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='linkblacklist', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='linkpermit', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='linkwhitelist', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='timer', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/strolchibot/models.py b/strolchibot/models.py index e5c8c956cc4107ff5db5704fcb2108a159abd501..d3eaad021c6c7a6e5d6190df8e78e6c624a6fe6a 100644 --- a/strolchibot/models.py +++ b/strolchibot/models.py @@ -1,10 +1,12 @@ +import os + from django.db import models -from .managers import TwitchUserManager + from strolchibot import twitch_api -import os +from .managers import TwitchUserManager -class TextCommand(models.Model): +class Command(models.Model): command = models.CharField(max_length=20) text = models.TextField(max_length=500) active = models.BooleanField(default=True) @@ -63,11 +65,22 @@ class TwitchUser(models.Model): @property def is_authenticated(self): - broadcaster_id = int(os.getenv("BROADCASTER_ID")) - if self.id == broadcaster_id or self.admin: + return self.is_broadcaster or self.is_admin or self.is_mod + + @property + def is_broadcaster(self): + return self.id == int(os.getenv("BROADCASTER_ID")) + + @property + def is_admin(self): + return self.admin + + @property + def is_mod(self): + if self.is_broadcaster: return True try: - broadcaster = TwitchUser.objects.get(pk=broadcaster_id) + broadcaster = TwitchUser.objects.get(pk=int(os.getenv("BROADCASTER_ID"))) return twitch_api.is_mod(self, broadcaster) except TwitchUser.DoesNotExist: return False diff --git a/strolchibot/settings.py b/strolchibot/settings.py index 555c4dac43ae6a1f2fba4d13d0f1cadbe8408b7b..69d34763e2d9c260bd562c314db7cb068953277f 100644 --- a/strolchibot/settings.py +++ b/strolchibot/settings.py @@ -109,6 +109,7 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ diff --git a/strolchibot/static/css/styles.css b/strolchibot/static/css/styles.css index 1ef4895c344db0446ee77ed49b4229760e849bdc..15526254400f7ff476d148ea55f19dd7bac106c9 100644 --- a/strolchibot/static/css/styles.css +++ b/strolchibot/static/css/styles.css @@ -12,11 +12,19 @@ } .w3-strolchpink:hover { - background: #d2619b!important; + background: #d2619b !important; +} + +.w3-strolchgray { + background: #ccc; +} + +.w3-strolchgray:hover { + background: #888 !important; } .w3-hover-strolchpink:hover, .w3-hover-strolchpink.active { - background: #e092b8!important; + background: #e092b8 !important; } #logo { @@ -24,7 +32,7 @@ } .w3-form-container { - margin-right: 50px; + margin-right: 120px; } .w3-display-topright .w3-button { @@ -109,4 +117,8 @@ input:checked + .slider:before { .tablink { text-align: center; +} + +.w3-hide, .w3-show { + transition: display 2s; } \ No newline at end of file diff --git a/strolchibot/static/js/scripts.js b/strolchibot/static/js/scripts.js index f58d91d3acb33bc2d1e684f1b5274ee0c9aa6a3b..d7c23e9cb1d3bcaf998fc7628546a6a0b3c0a727 100644 --- a/strolchibot/static/js/scripts.js +++ b/strolchibot/static/js/scripts.js @@ -13,4 +13,15 @@ document.querySelectorAll(".tablink").forEach(value => { document.getElementById(cityName).style.display = "block"; value.className += " active"; }) +}); + + +document.querySelectorAll(".accordeon").forEach(value => { + value.addEventListener("click", evt => { + var accordeonItem = document.querySelector("#" + value.id + "-element"); + accordeonItem.classList.toggle("w3-show"); + accordeonItem.classList.toggle("w3-hide"); + evt.target.classList.toggle("fa-chevron-down"); + evt.target.classList.toggle("fa-chevron-up"); + }); }); \ No newline at end of file diff --git a/strolchibot/templates/card_formset.html b/strolchibot/templates/card_formset.html index 9f71d1edd13743f5804068f5715d1110e63b0d69..faae214a5766bff69fcfc8c4f1b39832998e431e 100644 --- a/strolchibot/templates/card_formset.html +++ b/strolchibot/templates/card_formset.html @@ -4,6 +4,15 @@ <div class="w3-container w3-form-container"> {{ formset_form.non_field_errors }} {% for field in formset_form %} + {% if field.name != "active" %} + + {% if form.collapsible and not forloop.parentloop.last and forloop.counter0 == 1 %} + <a href="#" class="accordeon w3-display-bottommiddle" + id="accordeon-{{ forloop.parentloop.counter0 }}"> + <i class="fas fa-chevron-down"></i> + </a> + <div class="w3-hide" id="accordeon-{{ forloop.parentloop.counter0 }}-element"> + {% endif %} <p> {{ field.errors }} {% if field.widget_type == "checkbox" %} @@ -15,15 +24,30 @@ {% else %} {{ field }} {% endif %} - </p> + + + {% if form.collapsible and not forloop.parentloop.last and forloop.last %} + </div> + {% endif %} + {% endif %} {% endfor %} </div> {% if formset_form.fields.id.initial %} <div class="w3-display-topright"> - <p><a href="{% url form.remove_url formset_form.fields.id.initial %}" - class="w3-button w3-strolchpink"><i - class="fas fa-trash-alt"></i></a></p> + <p> + {% for field in formset_form %} + {% if field.name == "active" and form.activate_url %} + <a href="{% url form.activate_url formset_form.fields.id.initial %}" class="w3-button + {% if field.value %} w3-strolchpink {% else %} w3-strolchgray {% endif %}"> + <i class="fas fa-check-square"></i> + </a> + {% endif %} + {% endfor %} + + <a href="{% url form.remove_url formset_form.fields.id.initial %}" + class="w3-button w3-strolchpink"><i + class="fas fa-trash-alt"></i></a></p> </div> {% endif %} </div> diff --git a/strolchibot/templates/layout.html b/strolchibot/templates/layout.html index 026ca8a0c4d9143e2aa2b2b6f41d73cc1a070328..770f9be18c721e4c074462fec5a6883cbc17177f 100644 --- a/strolchibot/templates/layout.html +++ b/strolchibot/templates/layout.html @@ -12,11 +12,13 @@ </head> <body class="w3-light-gray w3-cursive"> <nav> -{% include 'navigation.html' %} + {% include 'navigation.html' %} </nav> <div id="content"> - <div class="w3-container w3-cursive"> + <div class="w3-container w3-cursive" style="position: relative;"> <h1 class="w3-cursive">{{ title }}</h1> + {% block title %} + {% endblock %} </div> <div class="w3-container"> {% block content %}{% endblock %} diff --git a/strolchibot/templates/navigation.html b/strolchibot/templates/navigation.html index 96c458cfc227f9d44b58c8702cf7767506f5eb36..9f4ed5316c1a4aa01a8b79385761829bb53ada33 100644 --- a/strolchibot/templates/navigation.html +++ b/strolchibot/templates/navigation.html @@ -2,9 +2,10 @@ <div class="w3-sidebar w3-bar-block w3-strolchdark "> <a href="{% url 'home' %}" class="logo"><img src="{% static 'images/logo.png' %}" id="logo"/></a> + {% if user.is_authenticated %} - <a href="{% url 'text_commands' %}" - class="w3-bar-item w3-button {% if title == "Text Commands" %}w3-light-gray{% endif %}">Text Commands</a> + <a href="{% url 'commands' %}" + class="w3-bar-item w3-button {% if title == "Commands" %}w3-light-gray{% endif %}">Commands</a> <a href="{% url 'klassenbuch' %}" class="w3-bar-item w3-button {% if title == "Klassenbuch" %}w3-light-gray{% endif %}">Klassenbuch</a> <a href="{% url 'timers' %}" @@ -15,8 +16,12 @@ <a href="{% url 'config' %}" class="w3-bar-item w3-button {% if title == "Config" %}w3-light-gray{% endif %}">Config</a> {% endif %} - <a href="{% url 'logout' %}" class="w3-bar-item w3-button">Logout</a> {% else %} <a href="{% url 'login' %}" class="w3-bar-item w3-button">Login</a> {% endif %} + <a href="{% url 'clips' %}" + class="w3-bar-item w3-button {% if title == "Clips" %}w3-light-gray{% endif %}">Clips</a> + {% if user.is_authenticated %} + <a href="{% url 'logout' %}" class="w3-bar-item w3-button">Logout</a> + {% endif %} </div> \ No newline at end of file diff --git a/strolchibot/urls.py b/strolchibot/urls.py index 7faf695b51bc2fbe01bbed2461141625813df88a..2f521cb141a7650256c99c72417fa4918dc7f8dd 100644 --- a/strolchibot/urls.py +++ b/strolchibot/urls.py @@ -26,18 +26,23 @@ urlpatterns = [ path('login/', views.login, name="login"), path('logout/', views.logout, name="logout"), path('login/redirect/', views.login_redirect, name="login_redirect"), - path('text_commands/', views.text_commands, name="text_commands"), - path('text_commands/remove/<int:id>', views.text_commands_remove, name="text_commands_remove"), + path('commands/', views.commands, name="commands"), + path('commands/remove/<int:id>', views.commands_remove, name="commands_remove"), + path('commands/activate/<int:id>', views.commands_activate, name="commands_activate"), path('klassenbuch/', views.klassenbuch, name="klassenbuch"), path('klassenbuch/remove/<int:id>', views.klassenbuch_remove, name="klassenbuch_remove"), path('timers/', views.timers, name="timers"), path('timers/remove/<int:id>', views.timers_remove, name="timers_remove"), + path('timers/activate/<int:id>', views.timers_activate, name="timers_activate"), path('config/', views.config, name="config"), path('config/remove/<int:id>', views.config_remove, name="config_remove"), path('link_protection/', views.link_protection, name="link_protection"), - path('link_protection/permit/remove/<int:id>', views.link_protection_permit_remove, name="link_protection_permit_remove"), - path('link_protection/whitelist/remove/<int:id>', views.link_protection_whitelist_remove, name="link_protection_whitelist_remove"), - path('link_protection/blacklist/remove/<int:id>', views.link_protection_blacklist_remove, name="link_protection_blacklist_remove"), + path('link_protection/permit/remove/<int:id>', views.link_protection_permit_remove, + name="link_protection_permit_remove"), + path('link_protection/whitelist/remove/<int:id>', views.link_protection_whitelist_remove, + name="link_protection_whitelist_remove"), + path('link_protection/blacklist/remove/<int:id>', views.link_protection_blacklist_remove, + name="link_protection_blacklist_remove"), path('strolchguru/', include('strolchguru.urls')), path('twitter/', include('twitter.urls')), ] diff --git a/strolchibot/views.py b/strolchibot/views.py index 16c98aeee1ad905bab674e0250714f16b2054eaf..8be46cf443c62959e04e8ea6cf800ed87b1db4c5 100644 --- a/strolchibot/views.py +++ b/strolchibot/views.py @@ -1,62 +1,79 @@ -from django.http import Http404 -from django.shortcuts import render, redirect -from django.forms import modelformset_factory +import os + +import requests from django.contrib.auth import authenticate, login as django_login, logout as django_logout from django.contrib.auth.decorators import login_required -from .models import TextCommand, Klassenbuch, Timer, Config, LinkPermit, LinkWhitelist, LinkBlacklist +from django.forms import modelformset_factory +from django.http import Http404 +from django.shortcuts import render, redirect + from .forms import BaseModelForm, LinkProtectionConfigForm -import os -import requests +from .models import Command, Klassenbuch, Timer, Config, LinkPermit, LinkWhitelist, LinkBlacklist def home(request): - return render(request, "home.html", {'title': 'Strolchibot'}) + return render(request, "home.html", {"title": "Strolchibot"}) @login_required(login_url="/login") -def text_commands(request): - TextCommandsFormSet = modelformset_factory(TextCommand, form=BaseModelForm, fields=('command', 'text', 'active'), - field_classes=['']) +def commands(request): + CommandsFormSet = modelformset_factory(Command, form=BaseModelForm, fields=("command", "text", "active")) if request.method == "POST": - formset = TextCommandsFormSet(request.POST, request.FILES) + formset = CommandsFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() - forms = {"Basic Configuration": { - "display": "card", - 'type': 'formset', - 'name': 'textcommands', - 'formset': TextCommandsFormSet(), - 'remove_url': 'text_commands_remove', }, + forms = { + "Basic Configuration": { + "display": "card", + "type": "formset", + "name": "commands", + "formset": CommandsFormSet(), + "remove_url": "commands_remove", + "activate_url": "commands_activate", + "collapsible": True, + + }, } - return render(request, "form.html", {'title': 'Text Commands', 'forms': forms, 'active': 'textcommands'}) + return render(request, "form.html", {"title": "Commands", "forms": forms, "active": "commands"}) @login_required(login_url="/login") -def text_commands_remove(request, id): - TextCommand.objects.filter(pk=id).delete() +def commands_remove(request, id): + Command.objects.filter(pk=id).delete() - return redirect("/text_commands") + return redirect("/commands") + + +@login_required(login_url="/login") +def commands_activate(request, id): + command = Command.objects.get(pk=id) + command.active = not command.active + command.save() + + return redirect("/commands") @login_required(login_url="/login") def klassenbuch(request): - KlassenbuchFormSet = modelformset_factory(Klassenbuch, form=BaseModelForm, fields=('name', 'sticker')) + KlassenbuchFormSet = modelformset_factory(Klassenbuch, form=BaseModelForm, fields=("name", "sticker")) if request.method == "POST": formset = KlassenbuchFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() - forms = {"Basic Configuration": { - "display": "card", - 'type': 'formset', - 'name': 'klassenbuch', - 'formset': KlassenbuchFormSet(), - 'remove_url': 'klassenbuch_remove', }, + forms = { + "Basic Configuration": { + "display": "card", + "type": "formset", + "name": "klassenbuch", + "formset": KlassenbuchFormSet(), + "remove_url": "klassenbuch_remove", + }, } - return render(request, "form.html", {'title': 'Klassenbuch', 'forms': forms, 'active': 'klassenbuch'}) + return render(request, "form.html", {"title": "Klassenbuch", "forms": forms, "active": "klassenbuch"}) @login_required(login_url="/login") @@ -68,21 +85,24 @@ def klassenbuch_remove(request, id): @login_required(login_url="/login") def timers(request): - TimerFormSet = modelformset_factory(Timer, form=BaseModelForm, fields=('text', 'active')) + TimerFormSet = modelformset_factory(Timer, form=BaseModelForm, fields=("text", "active")) if request.method == "POST": formset = TimerFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() - forms = {"Basic Configuration": { - "display": "card", - 'type': 'formset', - 'name': 'timers', - 'formset': TimerFormSet(), - 'remove_url': 'timers_remove', }, + forms = { + "Basic Configuration": { + "display": "card", + "type": "formset", + "name": "timers", + "formset": TimerFormSet(), + "remove_url": "timers_remove", + "activate_url": "timers_activate", + }, } - return render(request, "form.html", {'title': 'Timers', 'forms': forms, 'active': 'timers'}) + return render(request, "form.html", {"title": "Timers", "forms": forms, "active": "timers"}) @login_required(login_url="/login") @@ -92,24 +112,34 @@ def timers_remove(request, id): return redirect("/timers") +@login_required(login_url="/login") +def timers_activate(request, id): + timer = Timer.objects.get(pk=id) + timer.active = not timer.active + timer.save() + + return redirect("/timers") + + @login_required(login_url="/login") def config(request): if request.user.admin: - ConfigFormSet = modelformset_factory(Config, form=BaseModelForm, fields=('key', 'value')) + ConfigFormSet = modelformset_factory(Config, form=BaseModelForm, fields=("key", "value")) if request.method == "POST": formset = ConfigFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() - forms = {"Basic Configuration": { - "display": "card", - 'type': 'formset', - 'name': 'config', - 'formset': ConfigFormSet(), - 'remove_url': 'config_remove', }, + forms = { + "Basic Configuration": { + "display": "card", + "type": "formset", + "name": "config", + "formset": ConfigFormSet(), + "remove_url": "config_remove", }, } - return render(request, "form.html", {'title': 'Config', 'forms': forms, 'active': 'config', }) + return render(request, "form.html", {"title": "Config", "forms": forms, "active": "config", }) raise Http404 @@ -126,9 +156,9 @@ def config_remove(request, id): @login_required(login_url="/login") def link_protection(request): - LinkPermitFormSet = modelformset_factory(LinkPermit, form=BaseModelForm, fields=('nick',)) - LinkWhitelistFormSet = modelformset_factory(LinkWhitelist, form=BaseModelForm, fields=('url',)) - LinkBlacklistFormSet = modelformset_factory(LinkBlacklist, form=BaseModelForm, fields=('url',)) + LinkPermitFormSet = modelformset_factory(LinkPermit, form=BaseModelForm, fields=("nick",)) + LinkWhitelistFormSet = modelformset_factory(LinkWhitelist, form=BaseModelForm, fields=("url",)) + LinkBlacklistFormSet = modelformset_factory(LinkBlacklist, form=BaseModelForm, fields=("url",)) active = "config" form = None @@ -147,35 +177,36 @@ def link_protection(request): if form and form.is_valid(): form.save() - forms = {"Basic Configuration": { - "display": "card", - 'type': 'form', - 'name': 'config', - 'form': LinkProtectionConfigForm()}, + forms = { + "Basic Configuration": { + "display": "card", + "type": "form", + "name": "config", + "form": LinkProtectionConfigForm()}, "Permits": { - 'display': 'card', - 'type': 'formset', - 'name': 'permit', - 'formset': LinkPermitFormSet(), - 'remove_url': 'link_protection_permit_remove', + "display": "list", + "type": "formset", + "name": "permit", + "formset": LinkPermitFormSet(), + "remove_url": "link_protection_permit_remove", }, "Whitelist": { - 'display': 'list', - 'type': 'formset', - 'name': 'whitelist', - 'formset': LinkWhitelistFormSet(), - 'remove_url': 'link_protection_whitelist_remove', + "display": "list", + "type": "formset", + "name": "whitelist", + "formset": LinkWhitelistFormSet(), + "remove_url": "link_protection_whitelist_remove", }, "Blacklist": { - 'display': 'list', - 'type': 'formset', - 'name': 'blacklist', - 'formset': LinkBlacklistFormSet(), - 'remove_url': 'link_protection_blacklist_remove', + "display": "list", + "type": "formset", + "name": "blacklist", + "formset": LinkBlacklistFormSet(), + "remove_url": "link_protection_blacklist_remove", }, } - return render(request, "form.html", {'title': 'Link Protection', 'forms': forms, 'active': active}) + return render(request, "form.html", {"title": "Link Protection", "forms": forms, "active": active}) @login_required(login_url="/login") @@ -212,7 +243,7 @@ def logout(request): def login_redirect(request): - code = request.GET.get('code') + code = request.GET.get("code") user = exchange_code(code) if user: twitch_user = authenticate(request, user=user) @@ -232,13 +263,13 @@ def exchange_code(code): credentials = response.json() response = requests.get("https://api.twitch.tv/helix/users", headers={ - 'Authorization': f'Bearer {credentials["access_token"]}', - 'Client-Id': client_id + "Authorization": f"Bearer {credentials['access_token']}", + "Client-Id": client_id }) user = response.json()["data"][0] - return {'id': user['id'], 'login': user['login'], 'access_token': credentials['access_token'], - 'refresh_token': credentials['refresh_token']} + return {"id": user["id"], "login": user["login"], "access_token": credentials["access_token"], + "refresh_token": credentials["refresh_token"]} return None diff --git a/twitchbot/strolchibot.py b/twitchbot/strolchibot.py index 0f760708f6e51dc534067860321ee8f7ab7aaf6b..187261396728e0139ba1a362c0c669e11e672b1a 100644 --- a/twitchbot/strolchibot.py +++ b/twitchbot/strolchibot.py @@ -122,7 +122,7 @@ async def process_text_commands(message): conn = sqlite3.connect("db.sqlite3") c = conn.cursor() - c.execute('SELECT text from strolchibot_textcommand where command = ? and active is true', (command,)) + c.execute('SELECT text from strolchibot_command where command = ? and active is true', (command,)) texts = c.fetchall() if len(texts) > 0: text = random.choice(texts)[0]