ATProto.send: handle DMs, translate and send as chat messages

for #1024, #966, etc
dms
Ryan Barrett 2024-08-08 11:42:28 -07:00
rodzic 356903c3e9
commit 37c781a2df
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
2 zmienionych plików z 50 dodań i 36 usunięć

Wyświetl plik

@ -461,6 +461,10 @@ class ATProto(User, Protocol):
Doesn't deliver anywhere externally! Relays will receive this record
through ``subscribeRepos`` and then deliver it to AppView(s), which will
notify recipients as necessary.
Exceptions:
* ``flag``s are translated to ``createReport`` to the mod service
* DMs are translated to ``sendMessage`` to the chat service
"""
if util.domain_from_link(url) not in DOMAINS:
logger.info(f'Target PDS {url} is not us')
@ -527,6 +531,7 @@ class ATProto(User, Protocol):
# * delete actor => tombstone repo
# * flag => send report to mod service
# * stop-following => delete follow record (prepared above)
# * dm => chat message
verb = obj.as1.get('verb')
if verb == 'delete':
atp_base_id = (base_id if ATProto.owns_id(base_id)
@ -537,7 +542,11 @@ class ATProto(User, Protocol):
arroba.server.storage.tombstone_repo(repo)
return True
elif verb == 'flag':
if not record:
# _convert already logged
return False
if verb == 'flag':
logger.info(f'flag => createReport with {record}')
return to_cls.create_report(record, from_user=user)
@ -546,11 +555,13 @@ class ATProto(User, Protocol):
assert base_obj and base_obj.type == 'follow', base_obj
verb = 'delete'
# write commit
if not record:
# _convert already logged
return False
elif as1.is_dm(obj.as1):
# is_dm checked that `to` has one elem
to_id = as1.get_ids(base_obj_as1, 'to')[0]
assert to_id.startswith('did:'), to_id
return ATProto.send_chat(record, from_repo=repo, to_did=to_id)
# write commit
type = record['$type']
lex_type = LEXICONS[type]['type']
assert lex_type == 'record', f"Can't store {type} object of type {lex_type}"
@ -835,12 +846,14 @@ class ATProto(User, Protocol):
logger.info(f'Created report on {mod_host}: {json_dumps(output)}')
return True
def send_chat(self, msg, from_user):
@classmethod
def send_chat(cls, msg, from_repo, to_did):
"""Sends a chat message to this user.
Args:
msg (dict): ``chat.bsky.convo.defs#messageInput``
from_user (models.User)
from_repo (arroba.repo.Repo)
to_did (str)
Returns:
bool: True if the report was sent successfully, False if the flag's
@ -848,18 +861,11 @@ class ATProto(User, Protocol):
"""
assert msg['$type'] == 'chat.bsky.convo.defs#messageInput'
to_did = self.key.id()
from_did = from_user.get_copy(ATProto)
if not from_did or not from_user.is_enabled(ATProto):
return False
repo = arroba.server.storage.load_repo(from_did)
chat_host = os.environ['CHAT_HOST']
token = service_jwt(host=chat_host,
aud=os.environ['CHAT_DID'],
repo_did=from_did,
privkey=repo.signing_key)
repo_did=from_repo.did,
privkey=from_repo.signing_key)
client = Client(f'https://{chat_host}', truncate=True, headers={
'User-Agent': USER_AGENT,
'Authorization': f'Bearer {token}',
@ -876,5 +882,5 @@ class ATProto(User, Protocol):
util.interpret_http_exception(e)
return False
logger.info(f'Sent chat message from {from_user.handle} to {self.handle} {to_did}: {json_dumps(sent)}')
logger.info(f'Sent chat message from {from_repo.handle} to {to_did}: {json_dumps(sent)}')
return True

Wyświetl plik

@ -1741,8 +1741,7 @@ Sed tortor neque, aliquet quis posuere aliquam […]
'Authorization': ANY,
})
# sendMessage
@patch('requests.post', return_value=requests_response({
@patch('requests.post', return_value=requests_response({ # sendMessage
'id': 'chat456',
'rev': '22222222tef2d',
'sender': {'did': 'did:plc:user'},
@ -1764,18 +1763,25 @@ Sed tortor neque, aliquet quis posuere aliquam […]
}),
requests_response(DID_DOC),
])
def test_send_chat(self, mock_get, mock_post):
def test_send_dm_chat(self, mock_get, mock_post):
user = self.make_user_and_repo()
alice = ATProto(id='did:plc:alice')
self.assertTrue(alice.send_chat({
'$type': 'chat.bsky.convo.defs#messageInput',
'text': 'hello world',
}, from_user=user))
dm = Object(id='fake:dm', source_protocol='fake', our_as1={
'objectType': 'note',
'actor': user.key.id(),
'content': 'hello world',
'to': ['did:plc:alice'],
})
self.assertTrue(ATProto.send(dm, 'https://bsky.brid.gy/'))
headers = {
'Content-Type': 'application/json',
'User-Agent': common.USER_AGENT,
'Authorization': ANY,
}
mock_get.assert_any_call(
'https://chat.service.local/xrpc/chat.bsky.convo.getConvoForMembers?members=did%3Aplc%3Aalice',
json=None, data=None, headers=ANY)
json=None, data=None, headers=headers)
mock_post.assert_called_with(
'https://chat.service.local/xrpc/chat.bsky.convo.sendMessage',
json={
@ -1783,12 +1789,12 @@ Sed tortor neque, aliquet quis posuere aliquam […]
'message': {
'$type': 'chat.bsky.convo.defs#messageInput',
'text': 'hello world',
# unused
'createdAt': '2022-01-02T03:04:05.000Z',
'bridgyOriginalText': 'hello world',
'bridgyOriginalUrl': 'fake:dm',
},
}, data=None, headers={
'Content-Type': 'application/json',
'User-Agent': common.USER_AGENT,
'Authorization': ANY,
})
}, data=None, headers=headers)
# getConvoForMembers
@patch('requests.get', return_value=requests_response({
@ -1797,12 +1803,14 @@ Sed tortor neque, aliquet quis posuere aliquam […]
}, status=400))
def test_send_chat_recipient_disabled(self, mock_get):
user = self.make_user_and_repo()
alice = ATProto(id='did:plc:alice')
self.assertFalse(alice.send_chat({
'$type': 'chat.bsky.convo.defs#messageInput',
'text': 'hello world',
}, from_user=user))
dm = Object(id='fake:dm', source_protocol='fake', our_as1={
'objectType': 'note',
'actor': user.key.id(),
'content': 'hello world',
'to': ['did:plc:alice'],
})
self.assertFalse(ATProto.send(dm, 'https://bsky.brid.gy/'))
mock_get.assert_any_call(
'https://chat.service.local/xrpc/chat.bsky.convo.getConvoForMembers?members=did%3Aplc%3Aalice',