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]