diff --git a/src/plainui/forms.py b/src/plainui/forms.py
index f63b56dbbaf4765e1cc7fd8badda0da509d3e195..591c558336d344c253d99a1a9fe51f94cb18ae0d 100644
--- a/src/plainui/forms.py
+++ b/src/plainui/forms.py
@@ -265,7 +265,7 @@ class ReportForm(forms.Form):
         if kind == 'url':
             reported_content = self.cleaned_data['kind_data']
         elif kind == 'pn':
-            dm = DirectMessage.objects.get(pk=self.cleaned_data['kind_data'], recipient=request.user)
+            dm = DirectMessage.objects.get(pk=self.cleaned_data['kind_data'])
             reported_content = 'DM by %(sender)s (%(sender_id)s) to %(recipient)s (%(recipient_id)s) at %(timestamp)s (%(uuid)s).\n'
             reported_content += 'Subject: "%(subject)s"\nMessage: "%(body)s"'
             reported_content %= {
diff --git a/src/plainui/tests.py b/src/plainui/tests.py
index cd59057e9fb4a97edcf8b56550b8418341eb58d0..0dc5e996413bd94e9a8a2bff61d9bed77a361778 100644
--- a/src/plainui/tests.py
+++ b/src/plainui/tests.py
@@ -1021,7 +1021,7 @@ class ViewsTest(TestCase):
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['form']['recipient'].value(), 'blgl')
 
-    @override_settings(LANGUAGE_CODE='en')
+    @override_settings(LANGUAGE_CODE='en', AUTOBAN_KEYWORDS=['blork', re.compile('12.45')])
     def test_PersonalMessageSendView_post(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
@@ -1073,6 +1073,38 @@ class ViewsTest(TestCase):
         self.assertEqual(form.errors['subject'], ['This field is required.'])
         self.assertEqual(form.errors['body'], ['This field is required.'])
 
+        DirectMessage.objects.all().delete()
+        self.user.refresh_from_db()
+        self.assertFalse(self.user.shadow_banned)
+        self.assertEqual(len(mail.outbox), 0)
+        resp = self.client.post(reverse('plainui:personal_message_send', kwargs={'conf_slug': self.conf.slug}), {
+            'in_reply_to': '',
+            'recipient': 'testuser2',
+            'subject': 'blork',
+            'body': 'Body2',
+        })
+        self.assertRedirects(resp, reverse('plainui:personal_message', kwargs={'conf_slug': self.conf.slug}))
+        new_dm = DirectMessage.objects.get()
+        self.user.refresh_from_db()
+        self.assertTrue(self.user.shadow_banned)
+        self.assertTrue(new_dm.deleted_by_recipient, True)
+        self.assertEqual(len(mail.outbox), 1)
+
+        self.user.shadow_banned = False
+        self.user.save()
+        resp = self.client.post(reverse('plainui:personal_message_send', kwargs={'conf_slug': self.conf.slug}), {
+            'in_reply_to': '',
+            'recipient': 'testuser2',
+            'subject': 'this is ok',
+            'body': '12345',
+        })
+        self.assertRedirects(resp, reverse('plainui:personal_message', kwargs={'conf_slug': self.conf.slug}))
+        new_dm = DirectMessage.objects.get(subject='this is ok')
+        self.user.refresh_from_db()
+        self.assertTrue(self.user.shadow_banned)
+        self.assertTrue(new_dm.deleted_by_recipient, True)
+        self.assertEqual(len(mail.outbox), 2)
+
     def test_PersonalMessageShowView(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
@@ -1345,7 +1377,7 @@ class ViewsTest(TestCase):
         self.assertEqual(resp.context_data['form']['title'].value(), '')
         self.assertEqual(resp.context_data['form']['text'].value(), '')
 
-    @override_settings(LANGUAGE_CODE='en')
+    @override_settings(LANGUAGE_CODE='en', AUTOBAN_KEYWORDS=['foo 123', re.compile('asdf.foo')])
     def test_BoardEntryEditView_post(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
@@ -1400,6 +1432,33 @@ class ViewsTest(TestCase):
         self.assertEqual(new_entry.text, 'some texty text')
         self.assertSetsMessage(resp, 'Bulletin Board Entry created.')
 
+        BulletinBoardEntry.objects.all().delete()
+        self.user.refresh_from_db()
+        self.assertFalse(self.user.shadow_banned)
+        self.assertEqual(len(mail.outbox), 0)
+        self.assertNeedsLogin(reverse('plainui:board_entry_new', kwargs={'conf_slug': self.conf.slug}), post=True)
+        resp = self.client.post(reverse('plainui:board_entry_new', kwargs={'conf_slug': self.conf.slug}), {
+            'title': 'New Entry',
+            'text': 'foo 123',
+        })
+        new_entry = BulletinBoardEntry.objects.get()
+        self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)}))
+        self.user.refresh_from_db()
+        self.assertTrue(self.user.shadow_banned)
+        self.assertEqual(len(mail.outbox), 1)
+
+        self.user.shadow_banned = False
+        self.user.save()
+        self.assertNeedsLogin(reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)}), post=True)
+        resp = self.client.post(reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)}), {
+            'title': 'asdf4foo',
+            'text': 'blblblblbl',
+        })
+        self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)}))
+        self.user.refresh_from_db()
+        self.assertTrue(self.user.shadow_banned)
+        self.assertEqual(len(mail.outbox), 2)
+
     def test_BoardEntryDeleteView_get(self):
         self.assertNeedsLogin(reverse('plainui:board_entry_delete', kwargs={'conf_slug': self.conf.slug}))
         resp = self.client.get(reverse('plainui:board_entry_delete', kwargs={'conf_slug': self.conf.slug}))
diff --git a/src/plainui/utils.py b/src/plainui/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..c8455d1d8be8ba7cdae54fb1303ad51df3cff64b
--- /dev/null
+++ b/src/plainui/utils.py
@@ -0,0 +1,35 @@
+import re
+
+from django.conf import settings
+
+from .forms import ReportForm
+
+
+def check_message_content(conf, request, text, kind, kind_data):
+    try:
+        trigger_autoban = False
+        for pattern in settings.AUTOBAN_KEYWORDS:
+            if isinstance(pattern, re.Pattern):
+                trigger_autoban |= pattern.search(text) is not None
+            elif pattern in text:
+                trigger_autoban = True
+
+        if not trigger_autoban:
+            return False
+
+        request.user.shadow_banned = True
+        request.user.save(update_fields=['shadow_banned'])
+
+        report_form = ReportForm(conf=conf, data={
+            'kind': kind,
+            'kind_data': str(kind_data),
+            'category': 'abuse',
+            'message': 'Autoban triggered',
+            'message2': '.',
+        })
+        report_form.is_valid()
+
+        report_form.send_report_mail('plainui/registration/report_mail_body.txt', request)
+        return True
+    except Exception:
+        raise Exception("Boom")  # don't allow leaking eg. ValidationErrors that might be handled in a View
diff --git a/src/plainui/views.py b/src/plainui/views.py
index 0c0a6922ce1336f086683d480ffbaabcd5b496a9..2ee289bea7535b57ac18128ded3422b0ce1ae4b6 100644
--- a/src/plainui/views.py
+++ b/src/plainui/views.py
@@ -36,6 +36,7 @@ from .forms import BulletinBoardEntryForm, ExampleForm, InputTokenForm, NewDirec
     ProfileEditForm, SelfOrganizedSessionForm, RedeemTokenAddToUserForm, RedeemTokenUserCreateForm, ReportForm, \
     RedeemBadgeForm, TokenPasswortResetForm
 from .models import BulletinBoardEntry
+from .utils import check_message_content
 
 
 def _session_refresh_favorite_assemblies(session, user) -> List[str]:
@@ -852,6 +853,10 @@ class PersonalMessageSendView(ConferenceRequiredMixin, FormView):
         if in_reply_to:
             DirectMessage.objects.filter(id=in_reply_to, recipient=self.request.user).update(has_responded=True)
 
+        if check_message_content(self.conf, self.request, form.cleaned_data['subject'], 'pn', dm.pk) or \
+                check_message_content(self.conf, self.request, form.cleaned_data['body'], 'pn', dm.pk):
+            dm.deleted_by_recipient = True
+            dm.save(update_fields=['deleted_by_recipient'])
         messages.success(self.request, gettext("Message sent."))
         return super().form_valid(form)
 
@@ -1106,6 +1111,9 @@ class BoardEntryEditView(ConferenceRequiredMixin, FormView):
         self.board_entry.text = form.cleaned_data['text']
         self.board_entry.save()
 
+        check_message_content(self.conf, self.request, form.cleaned_data['title'], 'board', self.board_entry.pk)
+        check_message_content(self.conf, self.request, form.cleaned_data['text'], 'board', self.board_entry.pk)
+
         if 'id' in self.kwargs:
             messages.success(self.request, gettext("Bulletin Board Entry updated."))
         else:
diff --git a/src/rc3platform/settings/base.py b/src/rc3platform/settings/base.py
index 85f85aab470813be34c554a51ee056d435da4ac8..4551ff24a9806cfed2e4ad261d8f31546c25782e 100644
--- a/src/rc3platform/settings/base.py
+++ b/src/rc3platform/settings/base.py
@@ -189,6 +189,9 @@ REST_FRAMEWORK = {
 # List of disallowed assembly slugs
 FORBIDDEN_ASSEMBLY_SLUGS = ['visit', 'maps', 'api', 'pusher']
 
+# list of autoban keywords. (Usage will trigger a report and send an abuse mail). Use strings or re.compile
+AUTOBAN_KEYWORDS = []
+
 # Mail configuration
 MAIL_REPLY_TO = []
 SUPPORT_HTML_MAILS = False