decentralized and customizable links page on top of atproto

resolve handles via well-known

Changed files
+35 -10
src
atproto
+35 -10
src/atproto/__init__.py
··· 1 1 from aiodns import DNSResolver, error as dns_error 2 2 from aiohttp.client import ClientSession 3 + from asyncio import tasks 3 4 from os import getenv 4 5 from re import match as regex_match 5 6 from typing import Any ··· 35 36 36 37 if is_valid_handle(query): 37 38 handle = query.lower() 38 - did = await resolve_did_from_handle(handle, didkv) 39 + did = await resolve_did_from_handle(client, handle, didkv) 39 40 if not did: 40 41 return None 41 42 doc = await resolve_doc_from_did(client, did) ··· 54 55 handle = handle_from_doc(doc) 55 56 if not handle: 56 57 return None 57 - if await resolve_did_from_handle(handle, didkv) != did: 58 + if await resolve_did_from_handle(client, handle, didkv) != did: 58 59 return None 59 60 return (did, handle, doc) 60 61 ··· 73 74 74 75 75 76 async def resolve_did_from_handle( 77 + client: ClientSession, 76 78 handle: str, 77 79 kv: KV = nokv, 78 80 reload: bool = False, 79 81 ) -> str | None: 80 - """Returns the DID for a given handle""" 82 + """Returns the DID for a given handle.""" 81 83 82 84 if not is_valid_handle(handle): 83 85 return None ··· 86 88 if did is not None and not reload: 87 89 return did 88 90 89 - resolver = DNSResolver() 90 - try: 91 - result = await resolver.query(f"_atproto.{handle}", "TXT") 92 - except dns_error.DNSError: 93 - return None 91 + did = await _resolve_did_from_handle_dns(handle) 92 + if did is None: 93 + did = await _resolve_did_from_handle_wk(client, handle) 94 + 95 + if did is not None and is_valid_did(did): 96 + kv.set(handle, value=did) 97 + return did 98 + 99 + return None 100 + 101 + 102 + async def _resolve_did_from_handle_wk(client: ClientSession, handle: str) -> str | None: 103 + """Resolve the DID for a given handle via .well-known""" 104 + 105 + url = f"https://{handle}/.well-known/atproto-did" 106 + response = await client.get(url) 107 + if response.ok: 108 + return await response.text() 109 + return None 110 + 111 + 112 + async def _resolve_did_from_handle_dns(handle: str) -> str | None: 113 + """Resolve the DID for a given handle via DNS.""" 114 + 115 + async with DNSResolver() as resolver: 116 + try: 117 + result = await resolver.query(f"_atproto.{handle}", "TXT") 118 + except dns_error.DNSError: 119 + return None 94 120 95 121 for record in result: 96 - value = str(record.text).replace('"', "") 122 + value = str(record.text).strip('"') 97 123 if value.startswith("did="): 98 124 did = value[4:] 99 125 if is_valid_did(did): 100 - kv.set(handle, value=did) 101 126 return did 102 127 103 128 return None