Skip to content

Commit

Permalink
/update-profile: for Web users, try to set ATProto handle to domain
Browse files Browse the repository at this point in the history
for #826
  • Loading branch information
snarfed committed Oct 29, 2024
1 parent 65c19ef commit 2cd127b
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 24 deletions.
1 change: 1 addition & 0 deletions atproto.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ def set_username(to_cls, user, username):
logger.info(f'repo for {did} is tombstoned, giving up')
return False

logger.info(f'Setting ATProto handle for {user.key.id()} to {username}')
repo.callback = lambda _: common.create_task(queue='atproto-commit')
did.update_plc(did=copy_did, handle=username,
signing_key=repo.signing_key, rotation_key=repo.rotation_key,
Expand Down
32 changes: 20 additions & 12 deletions pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
canonicalize_request_domain,
error,
flash,
redirect,
)
import requests
import werkzeug.exceptions
Expand All @@ -27,6 +26,7 @@
import common
from common import CACHE_CONTROL, DOMAIN_RE
from flask_app import app
from flask import redirect
import ids
from models import fetch_objects, fetch_page, Follower, Object, PAGE_SIZE, PROTOCOLS
from protocol import Protocol
Expand Down Expand Up @@ -169,27 +169,35 @@ def notifications(protocol, id):
def update_profile(protocol, id):
user = load_user(protocol, id)
link = f'<a href="{user.web_url()}">{user.handle_or_id()}</a>'
redir = redirect(user.user_page_path(), code=302)

try:
user.reload_profile()
except (requests.RequestException, werkzeug.exceptions.HTTPException) as e:
_, msg = util.interpret_http_exception(e)
flash(f"Couldn't update profile for {link}: {msg}")
return redirect(user.user_page_path(), code=302)
return redir

if user.obj:
common.create_task(queue='receive', obj_id=user.obj_key.id(),
authed_as=user.key.id())
flash(f'Updating profile from {link}...')
if not user.obj:
flash(f"Couldn't update profile for {link}")
return redir

common.create_task(queue='receive', obj_id=user.obj_key.id(),
authed_as=user.key.id())
flash(f'Updating profile from {link}...')

if user.status and user.LABEL == 'web':
if user.LABEL == 'web':
if user.status:
logger.info(f'Disabling web user {user.key.id()}')
user.delete()

else:
flash(f"Couldn't update profile for {link}")

return redirect(user.user_page_path(), code=302)
else:
for label in list(user.DEFAULT_ENABLED_PROTOCOLS) + user.enabled_protocols:
try:
PROTOCOLS[label].set_username(user, id)
except (ValueError, RuntimeError, NotImplementedError) as e:
pass

return redir


@app.get(f'/<any({",".join(PROTOCOLS)}):protocol>/<id>/<any(followers,following):collection>')
Expand Down
6 changes: 3 additions & 3 deletions tests/test_dms.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def test_receive_username(self):
obj = Object(our_as1=DM_EFAKE_ALICE_SET_USERNAME_OTHER)
self.assertEqual(('OK', 200), receive(from_user=alice, obj=obj))
self.assert_replied(OtherFake, alice, '?', ALICE_USERNAME_CONFIRMATION_CONTENT)
self.assertEqual({OtherFake: 'new-handle'}, alice.usernames)
self.assertEqual({'efake:alice': 'new-handle'}, OtherFake.usernames)

def test_receive_username_not_implemented(self):
self.make_user(id='fa.brid.gy', cls=Web)
Expand All @@ -357,7 +357,7 @@ def test_receive_username_fails(self, _):
obj = Object(our_as1=DM_EFAKE_ALICE_SET_USERNAME_OTHER)
self.assertEqual(('OK', 200), receive(from_user=alice, obj=obj))
self.assert_replied(OtherFake, alice, '?', 'nopey')
self.assertEqual({}, alice.usernames)
self.assertEqual({}, OtherFake.usernames)

def test_receive_help(self):
self.make_user(id='other.brid.gy', cls=Web)
Expand All @@ -369,4 +369,4 @@ def test_receive_help(self):
})
self.assertEqual(('OK', 200), receive(from_user=alice, obj=obj))
self.assert_replied(OtherFake, alice, '?', "<p>Hi! I'm a friendly bot")
self.assertEqual({}, alice.usernames)
self.assertEqual({}, OtherFake.usernames)
10 changes: 8 additions & 2 deletions tests/test_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from requests import ConnectionError

# import first so that Fake is defined before URL routes are registered
from .testutil import Fake, TestCase, ACTOR, COMMENT, MENTION, NOTE
from .testutil import Fake, OtherFake, TestCase, ACTOR, COMMENT, MENTION, NOTE

from activitypub import ActivityPub
from atproto import ATProto
Expand Down Expand Up @@ -245,7 +245,11 @@ def test_update_profile_receive_task(self, mock_create_task):

@patch('requests.get', return_value=ACTOR_HTML_RESP)
def test_update_profile_web(self, mock_get):
self.user.obj.copies = [Target(protocol='fake', uri='fa:profile:web:user.com')]
self.user.obj.copies = [
Target(protocol='fake', uri='fa:profile:web:user.com'),
Target(protocol='other', uri='other:profile:web:user.com'),
]
self.user.enabled_protocols = ['other']
self.user.obj.put()
Follower.get_or_create(from_=self.make_user('fake:user', cls=Fake),
to=self.user)
Expand All @@ -270,6 +274,8 @@ def test_update_profile_web(self, mock_get):
'object': actor_as1,
})], Fake.sent)

self.assertEqual({'user.com': 'user.com'}, OtherFake.usernames)

@patch('requests.get', return_value=requests_response(
ACTOR_HTML.replace('Ms. ☕ Baz', 'Ms. ☕ Baz #nobridge'),
url='https://user.com/'))
Expand Down
14 changes: 7 additions & 7 deletions tests/testutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,8 @@ class Fake(User, protocol.Protocol):
fetched = []
created_for = []

# instance attr, dict mapping Protocol class to str username
usernames = None

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.usernames = {}
# maps str user id to str username
usernames = {}

@ndb.ComputedProperty
def handle(self):
Expand Down Expand Up @@ -222,6 +218,7 @@ class OtherFake(Fake):
sent = []
fetched = []
created_for = []
usernames = {}

@classmethod
def target_for(cls, obj, shared=False):
Expand All @@ -230,7 +227,8 @@ def target_for(cls, obj, shared=False):

@classmethod
def set_username(cls, user, username):
user.usernames[cls] = username
logger.info(f'Setting custom username for {user.key.id()} in {cls.LABEL} to {username}')
cls.usernames[user.key.id()] = username


class ExplicitFake(Fake):
Expand All @@ -245,6 +243,7 @@ class ExplicitFake(Fake):
sent = []
fetched = []
created_for = []
usernames = {}


# import other modules that register Flask handlers *after* Fake is defined
Expand Down Expand Up @@ -305,6 +304,7 @@ def setUp(self):
cls.sent = []
cls.fetched = []
cls.created_for = []
cls.usernames = {}

# make random test data deterministic
arroba.util._clockid = 17
Expand Down

0 comments on commit 2cd127b

Please sign in to comment.