Move utility functions out of views.py

This commit is contained in:
Jason McBrayer 2024-03-28 16:42:32 -04:00
parent 0c5f79c3f6
commit 223f3149cd
3 changed files with 229 additions and 211 deletions

1
.gitignore vendored
View File

@ -115,4 +115,5 @@ node_modules
package-lock.json package-lock.json
yarn.lock yarn.lock
/db.sqlite3-* /db.sqlite3-*
*~

215
brutaldon/utils.py Normal file
View File

@ -0,0 +1,215 @@
from requests import Session
from django.conf import settings as django_settings
from brutaldon.models import Client, Account
from mastodon import Mastodon, MastodonAPIError
from django.http import HttpResponseRedirect
import re
global sessons_cache
sessions_cache = {}
VISIBILITIES = ["direct", "private", "unlisted", "public"]
class NotLoggedInException(Exception):
pass
class LabeledList(list):
"""A subclass of list that can accept additional attributes"""
def __new__(self, *args, **kwargs):
return super(LabeledList, self).__new__(self, args, kwargs)
def __init(self, *args, **kwargs):
if len(args) == 1 and hasattr(args[0], "__iter__"):
list.__init__(self, args[0])
else:
list.__init__(self, args)
self.__dict__.update(kwargs)
def __call(self, **kwargs):
self.__dict__.update(kwargs)
return self
###
### Utility functions
###
def get_session(domain):
if domain in sessions_cache:
return sessions_cache[domain]
else:
s = Session()
sessions_cache[domain] = s
return s
def get_usercontext(request, feature_set="mainline"):
if is_logged_in(request):
try:
client = Client.objects.get(api_base_id=request.session["active_instance"])
user = Account.objects.get(username=request.session["active_username"])
except (
Client.DoesNotExist,
Client.MultipleObjectsReturned,
Account.DoesNotExist,
Account.MultipleObjectsReturned,
):
raise NotLoggedInException()
mastodon = Mastodon(
client_id=client.client_id,
client_secret=client.client_secret,
access_token=user.access_token,
api_base_url=client.api_base_id,
session=get_session(client.api_base_id),
ratelimit_method="throw",
feature_set=feature_set,
)
return user, mastodon
else:
return None, None
def is_logged_in(request):
return request.session.has_key("active_user")
def _notes_count(account, mastodon):
if not mastodon:
return ""
notes = mastodon.notifications(limit=40)
if account.preferences.filter_notifications:
notes = [
note for note in notes if note.type == "mention" or note.type == "follow"
]
for index, item in enumerate(notes):
if account.note_seen is None:
account.note_seen = "0"
account.save()
if str(item.id) <= str(account.note_seen):
break
else:
index = "40+"
return str(index)
def br_login_required(function=None, home_url=None, redirect_field_name=None):
"""Check that the user is logged in to a Mastodon instance.
This decorator ensures that the view functions it is called on can be
accessed only by logged in users. When an instanceless user accesses
such a protected view, they are redirected to the address specified in
the field named in `next_field` or, lacking such a value, the URL in
`home_url`, or the `ANONYMOUS_HOME_URL` setting.
"""
if home_url is None:
home_url = django_settings.ANONYMOUS_HOME_URL
def _dec(view_func):
def _view(request, *args, **kwargs):
def not_logged_in():
url = None
if redirect_field_name and redirect_field_name in request.REQUEST:
url = request.REQUEST[redirect_field_name]
if not url:
url = home_url
if not url:
url = "/"
return HttpResponseRedirect(url)
if not is_logged_in(request):
return not_logged_in()
else:
try:
return view_func(request, *args, **kwargs)
except MastodonAPIError:
# mastodon must have expired our session
return not_logged_in()
_view.__name__ = view_func.__name__
_view.__dict__ = view_func.__dict__
_view.__doc__ = view_func.__doc__
return _view
if function is None:
return _dec
else:
return _dec(function)
def min_visibility(visibility1, visibility2):
return VISIBILITIES[
min(VISIBILITIES.index(visibility1), VISIBILITIES.index(visibility2))
]
def get_filters(mastodon, context=None):
try:
if context:
return [ff for ff in mastodon.filters() if context in ff.context]
else:
return mastodon.filters()
except:
return []
def toot_matches_filters(toot, filters=[]):
if not filters:
return False
def maybe_rewrite_filter(filter):
if filter.whole_word:
return f"\\b{filter.phrase}\\b"
else:
return filter.phrase
phrases = [maybe_rewrite_filter(x) for x in filters]
pattern = "|".join(phrases)
try:
if toot.get("type") in ["reblog", "favourite"]:
return re.search(
pattern, toot.status.spoiler_text + toot.status.content, re.I
)
return re.search(pattern, toot.spoiler_text + toot.content, re.I)
except:
return False
def switch_accounts(request, new_account):
"""Try to switch accounts to the specified account, if it is already in
the user's session. Sets up new session variables. Returns boolean success
code."""
accounts_dict = request.session.get("accounts_dict")
if not accounts_dict or not new_account in accounts_dict.keys():
return False
try:
account = Account.objects.get(id=accounts_dict[new_account]["account_id"])
if account.username != new_account:
return False
except Account.DoesNotExist:
return False
request.session["active_username"] = account.username
request.session["active_instance"] = account.client.api_base_id
account, mastodon = get_usercontext(request)
request.session["active_user"] = mastodon.account_verify_credentials()
accounts_dict[new_account]["user"] = request.session["active_user"]
request.session["accounts_dict"] = accounts_dict
return True
def same_username(account, acct, username):
if acct == username:
return True
try:
user, host = username.split("@", 1)
except ValueError:
user, host = username, ""
myhost = account.username.split("@", 1)[1]
if acct == user and host == myhost:
return True
return False

View File

@ -4,7 +4,6 @@ from django.conf import settings as django_settings
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.urls import reverse from django.urls import reverse
from django.views.decorators.cache import never_cache, cache_page from django.views.decorators.cache import never_cache, cache_page
from django.core.files.uploadhandler import TemporaryFileUploadHandler
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from brutaldon.forms import ( from brutaldon.forms import (
LoginForm, LoginForm,
@ -21,149 +20,26 @@ from mastodon import (
MastodonAPIError, MastodonAPIError,
MastodonNotFoundError, MastodonNotFoundError,
) )
from brutaldon.utils import (
NotLoggedInException,
LabeledList,
get_usercontext,
_notes_count,
br_login_required,
min_visibility,
get_filters,
switch_accounts,
toot_matches_filters,
same_username,
)
from urllib import parse from urllib import parse
from itertools import groupby from itertools import groupby
from inscriptis import get_text from inscriptis import get_text
from time import sleep from time import sleep
from requests import Session
import re import re
class NotLoggedInException(Exception):
pass
class LabeledList(list):
"""A subclass of list that can accept additional attributes"""
def __new__(self, *args, **kwargs):
return super(LabeledList, self).__new__(self, args, kwargs)
def __init(self, *args, **kwargs):
if len(args) == 1 and hasattr(args[0], "__iter__"):
list.__init__(self, args[0])
else:
list.__init__(self, args)
self.__dict__.update(kwargs)
def __call(self, **kwargs):
self.__dict__.update(kwargs)
return self
global sessons_cache
sessions_cache = {}
VISIBILITIES = ["direct", "private", "unlisted", "public"]
###
### Utility functions
###
def get_session(domain):
if domain in sessions_cache:
return sessions_cache[domain]
else:
s = Session()
sessions_cache[domain] = s
return s
def get_usercontext(request, feature_set="mainline"):
if is_logged_in(request):
try:
client = Client.objects.get(api_base_id=request.session["active_instance"])
user = Account.objects.get(username=request.session["active_username"])
except (
Client.DoesNotExist,
Client.MultipleObjectsReturned,
Account.DoesNotExist,
Account.MultipleObjectsReturned,
):
raise NotLoggedInException()
mastodon = Mastodon(
client_id=client.client_id,
client_secret=client.client_secret,
access_token=user.access_token,
api_base_url=client.api_base_id,
session=get_session(client.api_base_id),
ratelimit_method="throw",
feature_set=feature_set,
)
return user, mastodon
else:
return None, None
def is_logged_in(request):
return request.session.has_key("active_user")
def _notes_count(account, mastodon):
if not mastodon:
return ""
notes = mastodon.notifications(limit=40)
if account.preferences.filter_notifications:
notes = [
note for note in notes if note.type == "mention" or note.type == "follow"
]
for index, item in enumerate(notes):
if account.note_seen is None:
account.note_seen = "0"
account.save()
if str(item.id) <= str(account.note_seen):
break
else:
index = "40+"
return str(index)
def br_login_required(function=None, home_url=None, redirect_field_name=None):
"""Check that the user is logged in to a Mastodon instance.
This decorator ensures that the view functions it is called on can be
accessed only by logged in users. When an instanceless user accesses
such a protected view, they are redirected to the address specified in
the field named in `next_field` or, lacking such a value, the URL in
`home_url`, or the `ANONYMOUS_HOME_URL` setting.
"""
if home_url is None:
home_url = django_settings.ANONYMOUS_HOME_URL
def _dec(view_func):
def _view(request, *args, **kwargs):
def not_logged_in():
url = None
if redirect_field_name and redirect_field_name in request.REQUEST:
url = request.REQUEST[redirect_field_name]
if not url:
url = home_url
if not url:
url = "/"
return HttpResponseRedirect(url)
if not is_logged_in(request):
return not_logged_in()
else:
try:
return view_func(request, *args, **kwargs)
except MastodonAPIError:
# mastodon must have expired our session
return not_logged_in()
_view.__name__ = view_func.__name__
_view.__dict__ = view_func.__dict__
_view.__doc__ = view_func.__doc__
return _view
if function is None:
return _dec
else:
return _dec(function)
def user_search(request): def user_search(request):
check = request.POST.get("status", "").split() check = request.POST.get("status", "").split()
if len(check): if len(check):
@ -191,12 +67,6 @@ def user_search_inner(request, query):
) )
def min_visibility(visibility1, visibility2):
return VISIBILITIES[
min(VISIBILITIES.index(visibility1), VISIBILITIES.index(visibility2))
]
def timeline( def timeline(
request, request,
timeline="home", timeline="home",
@ -253,61 +123,6 @@ def timeline(
) )
def get_filters(mastodon, context=None):
try:
if context:
return [ff for ff in mastodon.filters() if context in ff.context]
else:
return mastodon.filters()
except:
return []
def toot_matches_filters(toot, filters=[]):
if not filters:
return False
def maybe_rewrite_filter(filter):
if filter.whole_word:
return f"\\b{filter.phrase}\\b"
else:
return filter.phrase
phrases = [maybe_rewrite_filter(x) for x in filters]
pattern = "|".join(phrases)
try:
if toot.get("type") in ["reblog", "favourite"]:
return re.search(
pattern, toot.status.spoiler_text + toot.status.content, re.I
)
return re.search(pattern, toot.spoiler_text + toot.content, re.I)
except:
return False
def switch_accounts(request, new_account):
"""Try to switch accounts to the specified account, if it is already in
the user's session. Sets up new session variables. Returns boolean success
code."""
accounts_dict = request.session.get("accounts_dict")
if not accounts_dict or not new_account in accounts_dict.keys():
return False
try:
account = Account.objects.get(id=accounts_dict[new_account]["account_id"])
if account.username != new_account:
return False
except Account.DoesNotExist:
return False
request.session["active_username"] = account.username
request.session["active_instance"] = account.client.api_base_id
account, mastodon = get_usercontext(request)
request.session["active_user"] = mastodon.account_verify_credentials()
accounts_dict[new_account]["user"] = request.session["active_user"]
request.session["accounts_dict"] = accounts_dict
return True
def forget_account(request, account_name): def forget_account(request, account_name):
"""Forget that you were logged into an account. If it's the last one, log out """Forget that you were logged into an account. If it's the last one, log out
entirely. Sets up session variables. Returns a redirect to the correct entirely. Sets up session variables. Returns a redirect to the correct
@ -718,19 +533,6 @@ def thread(request, id):
) )
def same_username(account, acct, username):
if acct == username:
return True
try:
user, host = username.split("@", 1)
except ValueError:
user, host = username, ""
myhost = account.username.split("@", 1)[1]
if acct == user and host == myhost:
return True
return False
@br_login_required @br_login_required
def user(request, username, prev=None, next=None): def user(request, username, prev=None, next=None):
try: try: