diff --git a/brutaldon/templates/main/toot_partial.html b/brutaldon/templates/main/toot_partial.html
index b63d6da..3097061 100644
--- a/brutaldon/templates/main/toot_partial.html
+++ b/brutaldon/templates/main/toot_partial.html
@@ -1,5 +1,6 @@
{% load humanize %}
{% load sanitizer %}
+{% load taglinks %}
{% endif %}
- {{ toot.content | strip_html | safe }}
+ {{ toot.content |relink_toot | strip_html | safe }}
{% if toot.media_attachments %}
diff --git a/brutaldon/templatetags/__init__.py b/brutaldon/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/brutaldon/templatetags/taglinks.py b/brutaldon/templatetags/taglinks.py
new file mode 100644
index 0000000..d883b78
--- /dev/null
+++ b/brutaldon/templatetags/taglinks.py
@@ -0,0 +1,25 @@
+from django import template
+from bs4 import BeautifulSoup
+from urllib import parse
+from django.urls import reverse
+
+register = template.Library()
+
+@register.filter
+def relink_tags(value):
+ '''Treat the text as html, and replace tag links with app-internal tag links
+
+ Currently, this only works for tags in toots coming from Mastodon servers,
+ not necessarily GNU Social, Pleroma, or other fediverse servers, because
+ it relies on the markup that Mastodon puts on tags.
+
+ FIXME: handle arbitrary tag lengths.
+ '''
+ soup = BeautifulSoup(value, 'html.parser')
+ for link in soup.find_all('a', class_='hashtag'):
+ link['href'] = reverse('tag', args=[link.span.string])
+ return str(soup)
+
+@register.filter
+def relink_toot(value):
+ return relink_tags(value)
diff --git a/requirements.txt b/requirements.txt
index 8ec8424..feeeeac 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,12 +1,15 @@
+beautifulsoup4==4.6.0
bleach==2.1.3
certifi==2017.11.5
chardet==3.0.4
decorator==4.1.2
Django==2.0.4
django-html-sanitizer==0.1.5
+django-markdownify==0.8.0
django-widget-tweaks==1.4.2
html5lib==1.0.1
idna==2.6
+Markdown==2.6.11
Mastodon.py==1.2.1
python-dateutil==2.6.1
pytz==2017.3